npx create-react-app react-hooks-example
cd react-hooks-example // 리액트 내부 라이브러리라 설치 필요x
code . -r
class component로 state 처리
src => components => Example1.jsx =>
import React from "react";
export default class Example1 extends React.Component {
state = {
count: 0,
};
render() {
const { count } = this.state;
return (
<div>
<p>You Clicke {count} times </p>
<button onClick={this.click}>Click me</button>
</div>
);
}
click = () => {
this.setState({ count: this.state.count + 1 });
};
}
function component로 state 처리
src => components => Example2.jsx =>
import React from "react";
export default function Example2() {
const [count, setCount] = React.useState(0);
return (
<div>
<p>You Clicke {count} times </p>
<button onClick={click}>Click me</button>
</div>
);
function click() {
setCount(count + 1);
}
}
src => components => Example3.jsx =>
import React from "react";
// 현재 useState => count
// useState => {count : 0 } 바꾸기
export default function Example3() {
const [state, setState] = React.useState({ count: 0 });
return (
<div>
<p>You Clicke {state.count} times </p>
<button onClick={click}>Click me</button>
</div>
);
function click() {
setState({ count: state.count + 1 });
}
}
/*
함수의 인자로 state를 받으니까 state에 의존적으로 처리하지 않음
function click() {
setState((state) => ({
return {
count: state.count + 1,
};
});
}
*/
렌더 사이에서 바뀐 state를 정확히 표현해야한다면
렌더 사이에서 state를 ref 삼아 활용할 땐 좋지 않음
src => components => Example5.jsx =>
import React from "react";
export default function Example5() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log("componentDidMount1", count);
}); // react dependency list(빈 배열) => 최초에만 실행
// 없으면 렌더 후 항상 실행
React.useEffect(() => {
console.log("componentDidMount2", count);
}, []);
React.useEffect(() => {
console.log("componentDidMount & componentDidUpdate by count", count);
}, [count]); // count가 바뀔 때만 update되어 useEffect 실행
// componentWillUnmount
React.useEffect(() => {
console.log("componentDidMount & componentDidUpdate by count", count);
return () => {
// cleanUp
// useEffeect안의 함수가 새로운 함수를 리턴하도록
// 다음 render, dependency에 의해 useEffect가 실행될 때 먼저 실행된 후 다음 useEffeect가 실행
// dependency가 비었으므로 최초 한번만 실행 후 실행한 함수 컴포넌트가 사라질 때 될 때 return부분 실행
// == componentWillUnmount
console.log("cleanup by count", count);
};
}, []);
React.useEffect(() => {
console.log("componentDidMount & componentDidUpdate by count", count);
return () => {
//cleanup
console.log("cleanup by count", count);
};
}, [count]);
// 최초 실행시 ->> componentDidMount & componentDidUpdate by count", 0
// 두번째 렌더시 ->> cleanup by count" 0 , componentDidMount & componentDidUpdate by count", 1
return (
<div>
<p>You Clicke {count} times </p>
<button onClick={click}>Click me</button>
</div>
);
function click() {
setCount(count + 1);
}
}
useEffect :
dependency , clean up 이해하기
브라우저의 가로창 사이즈가 변경되었을 때 변경된 사이즈의 숫자를 가져오는 hook 만들기
src => hooks=> useWindowWidth.js
hook안에 다른 훅을 쓸 수 있다.
import React, { useState, useEffect } from "react";
export default function useWindowWidth() {
const [width, setWidth] = React.useState(window.innerWidth);
// // 사이즈 변경될 때마다 출력하기
useEffect(() => {
const resize = () => {
setWidth(window.innerWidth);
};
window.addEventListener("resize", resize);
//cleanup
return () => {
window.removeEventListener("resize", resize);
};
}, []);
return width;
}
App.js =>
function App() {
const width = useWindowWidth();
return (
...
{width}
)
}
hooks => useHasMounted.js
src => hocs => withHasMounted.jsx
import React from "react";
export default function withHasMounted(Component) {
class NewComponent extends React.Component {
state = {
hasMounted: false,
};
render() {
const { hasMounted } = this.state;
return <Component {...this.props} hasMounted={hasMounted} />;
}
componentDidMount() {
this.setState({ hasMounted: true });
}
}
NewComponent.displayName = `withHasMounted(${Component.name})`;
return NewComponent;
}
useHasMounted.js =>
import { useEffect, useState } from "react";
export default function useHasMounted() {
const [hasMounted, setHasMounted] = useState(false);
useEffect(() => {
setHasMounted(true);
}, []);
return hasMounted;
}
App.js =>
function App({hasMounted}){
const hasMountedFromHook = useHasMounted();
console.log(hasMounted, hasMountedfromHooks);
}
useState의 확장판
다수의 하위 값을 포함하는 복잡한 정적로직을 만들때
다음스테이트가 이전 스테이트에 의존적인경우
리덕스를 안다면 쉽게 사용가능
component => Example6.jsx
// reducer => state를 변경하는 로직이 담겨있는 함수
const reducer = (state, action) =>{
if (action.type === "PLUS") {
return {
count: state.count + 1
}
}
return state;
}
// dispatch => action 객체를 넣어서 실행
// action => 보통 객체이고 필수 프로퍼ㅌ로 type을 가진다.
export default function Example6() {
const [state, dispatch] = useReducer(reducer, {count:0})
return (
<div>
<p>You Clicke {state.count} times </p>
<button onClick={click}>Click me</button>
</div>
)
function click() {
dispatch({type: "PLUS"}) =
}
}
component => Example7.jsx
function sum(persons) {
console.log('sum...')
return persons.map(person => person.age).reduce( (l,r) => l + r, 0 )
}
export default function Example7 () {
const [value,setValue] = useState('')
const [persons] = useState([
{name: 'Mark', age:39},
{name: 'hanna', age:28},
])
const count = useMemo(() => {
return sum(persons);
} , [persons])
const click = useCallback( () => {
console.log(value)
}, [])
return (
<div>
<input value={} onChange={} />
<p>{count}</p>
<button onClick={click}> click </button>
</div>
)
function change(e) {
setValue(e.target.value)
}
}
components => Example8.jsx
export default function Example8 () {
const [value,setValue] = useState('')
const input1Ref = createRef()
const input1Ref = useRef()
console.log(input1Ref.current, input2Ref.current)
return (
<div>
<input value={} onChange={} />
<input ref={input1Ref} />
<input ref={input2Ref} />
</div>
)
function change(e) {
setValue(e.target.value)
}
}
React Router Example =>
useHistory :props로 들어오는 히스토리랑 ㅇ같은 히스토리를 구해서 주는 Hook
= withRouter
const history = useHistory()
Profile =>
const params = useParams()
npx create-react-app component-comunication
cd component-comunication
code . -r
npm start
src => components => A.jsx
export default function A() {
const [value] = useState('아직 안바뀜')
return (
<div>
<B value={value}/>
<Button onclick= /> E의 값을 바꾸기 </Button>
</div>
)
}
function B() {
return (
<div>
<p>여긴 B</p>
<C/>
</div>
)
}
function C() {
return (
<div>
<p>여긴 C</p>
<D/>
</div>
)
}
function D() {
return (
<div>
<p>여긴 D</p>
<E/>
</div>
)
}
function E() {
return (
<div>
<p>여긴 E</p>
<h3>value</h3>
</div>
)
}
App.js =>
```js
return (
<A />
)
컴포넌트간 통신을 쉽게 도와줌
npx create-react-app react-context-example
cd react-context-example
code . -r
npm start
src => contexts => PersonContext.js
import React from 'react'
const PersonContext = React.createContext()
export default PersonContext;
index.js =>
const persons = [
{id: 0, name: 'Mark', age: 39}
{id: 1, name: 'Hanna', age: 28}
]
<PersonContext.Provider value={persons}>
<App />
<PersonContext.Provider>
components => Example1.jsx
export default function Example1() {
return (
<PersonContext.Consumer>
{ (persons) => (
<ul>
{persons.map( (person) => (
<li>{person.name}</ li>
))}
</ul>
)}
<PersonContext.Consumer>
)
}
// class, function 컴포넌트 다 사용가능
components => Example2.jsx
export default class Example2 extends React.Component {
// static contextType = PersonContext; // class 컴포넌트에서만
render() {
const persons = this.context;
return (
<ul>
{persons.map( (person) => (
<li>{person.name}</ li>
))}
</ul>
)
}
}
// function 컴포넌트에서 사용하고 싶다면
// Example2.contextType = PersonContext
단점 = static contextType는 여러개를 지정못함
만약 다른 context에서 데이터를 동시에 가져와 사용하고 싶다면 컴포넌트를 하나 더 만들어서 가져와야함.
가장 많이 쓰임
components => Example3.jsx
export default function Example3() {
const persons = useContext(PersonContext)
return (
<ul>
{persons.map( (person) => (
<li>{person.name}</ li>
))}
</ul>
)
}