const [state, setState] = useState(initialState);
initialState: 상태값의 초기값(초기 렌더링 후 무시됨)state: 저장된 상태값setState: 상태값을 변경하는 setter 함수
componentDidMount(): 컴포넌트 마운트 완료되고 브라우저 DOM 트리에 반영된 후 호출componentDidUpdate(): 브라우저 DOM 업데이트 완료 후 호출componentWillUnmount(): 컴포넌트가 삭제되기 직전에 호출useEffect(setup, dependencies?);
setup: 컴포넌트가 마운트(1-4), 업데이트(2-5), 제거(3-1) 될 때 호출되는 함수
setup이 함수를 리턴하면, 리턴한 함수를 cleanup이라고 부르며, 컴포넌트가 업데이트되거나 언마운트 될 때 호출됨 (cleanup이 먼저 실행되고 setup이 뒤에 실행됨)dependencies (선택): 의존 객체 배열
setup 함수를 호출할지 말지 여부를 결정하는데 사용dependencies 여부와 상관 없이 setup이 호출됨dependencies를 생략하면, 컴포넌트가 업데이트될 때 항상 setup이 호출됨dependencies에 빈 배열을 지정하면 업데이트에서는 호출되지 않음dependencies를 지정하면, 해당 값들이 변경될 때만 setup 함수가 호출됨useState와 비슷하지만, 상태 관리가 더 복잡한 경우에 사용useState를 사용할 때 컴포넌트 내부에서 상태 변경 로직을 구현해야 하기 때문에 컴포넌트가 복잡해짐state 값은 불변성이 있어 상태 변경의 내역을 추적할 수 있음Reduxfunction reducer(state, action){ ... }
const [state, dispatch] = useReducer(reducer, initialArg, init?);
reducer: state와 action을 인자로 받아 새로운 state를 반환하는 함수
state: 리듀서에 전달되는 상태값action: dispatch에 전달한 인자값으로, 수행할 작업의 종류와 필요한 인자값을 포함한 객체initialArg: 초기 상태로 지정할 값init(선택): initialArg를 인자로 받고, 리턴한 값이 초기 상태로 지정되는 함수state: 상태값이 저장된 getterdispatch: 상태값을 변경하는 setter 함수. dispatch에 전달한 인자값이 reducer의 두번째 인자값(action)으로 전달됨useReducer를 사용하면 reducer 함수와 dispatch 액션을 작성해야 하기 때문에 기본적으로 코드 크기가 useState를 사용할 때보다 많아짐
const TodoReducer = function(state, action) {
// 상태 변경 로직
};
itemListDispatch({ type: 'TOGGLE', item: { _id }});
reducer 함수에 공통으로 작성해서 코드를 줄일 수 있음
const TodoReducer = function(state, action){
const index = state.findIndex(item => item._id === action.item._id);
switch(action.type){
case 'TOGGLE':
return produce(state, draft => {
draft[index].done = !draft[index].done;
});
default:
case 'DELETE':
return produce(state, draft => {
draft.splice(index, 1);
});
}
};
useState로 상태를 직접 관리하기보다는 useReducer에 상태 변경 로직을 집중시키고 컴포넌트와 분리하면 컴포넌트를 단순화할 수 있음useState는 상태 변경 도중 오류가 발생하면 여러 이벤트 핸들러를 확인해야 하지만, useReducer는 상태 변경 로직이 한 곳에 있기 때문에 디버깅이 편함reducer 함수는 컴포넌트와 독립적인 순수 함수이기 때문에 따로 테스트가 가능함useState는 값이 변경되면 컴포넌트가 다시 렌더링되지만 useRef는 값이 변경되어도 컴포넌트가 다시 렌더링되지 않음ref 속성을 추가하면 브라우저 DOM 엘리먼트에 직접 접근 가능
useRef를 사용해 브라우저 DOM에 직접 접근하여 제어해야 함const ref = useRef(initialValue);
initialValue: 초기값current라는 상태값 또는 DOM 요소가 있는 속성 하나가 정의된 객체function App(){
const [msg, setMsg] = useState('');
return (
<>
<h1>01 useState - 상태 관리</h1>
<div>
<input type="text" value={ msg } onChange={ e => setMsg(e.target.value) } />
<br/>
<span>입력 메세지: { msg }</span>
</div>
</>
);
}
function Counter() {
const step = useRef(1);
return (
<div id="counter">
<input type="number" defaultValue={ step.current } onChange={ e => step.current = Number(e.target.value) } />
<Button color="red">-</Button>
<Button>0</Button>
<Button color="blue">+</Button>
<span>0</span>
</div>
);
}
const calculateValue = function(){ ... };
const cachedValue = useMemo(calculateValue, dependencies);
calculateValue: 캐싱할 값을 계산하여 반환하는 순수 함수dependencies: 의존 객체 배열
calculateValue 함수의 인자값calculateValue 함수를 다시 호출하고, 변경되지 않으면 캐시된 값을 반환calculateValue 함수를 호출한 결과값dependencies가 변경되지 않으면 캐시된 결과를, 변경되었으면 calculateValue 함수를 다시 호출한 결과값const MemoizedComponent = React.memo(SomeComponent, arePropsEqual?)
SomeComponent: memoize할 대상 컴포넌트arePropsEqual(선택): memoize된 컴포넌트를 반환할지, 컴포넌트를 다시 호출할지를 결정하는 함수
true를 반환하면 memoize된 컴포넌트를 사용하고, false를 반환하면 컴포넌트를 다시 호출한 결과값을 사용true, 다르면 false를 반환하는 기본 동작SomeComponentArray.prototype.forEach, Array.prototype.map, Array.prototype.findIndex, Array.prototype.filter 등.
리액트에서의 활용
React.memo가 대표적인 고차 함수.useCallback()을 사용하면 부모 컴포넌트가 재호출되어도 리스너가 수정되지 않고 유지되므로 자식도 기존 DOM을 재사용하여 성능이 향상됨const cachedFn = useCallback(fn, dependencies);
fn: 캐싱할 함수dependencies: 의존 객체 배열
fn 함수를 반환하고, 다음 렌더링부터는 dependencies가 변하지 않았다면 이전에 반환한 캐시된 함수를, dependencies가 변했다면 새로 생성된 fn 함수를 반환useMemo는 함수를 인자로 전달하고, 전달된 함수의 실행 결과(리턴값)를 memoize 함React.memo는 컴포넌트를 인자로 전달하고, 전달된 컴포넌트를 memoize 함useCallback은 함수를 인자로 전달하고, 전달된 함수를 memoize 함useState, useEffect 등)을 이용해 특정 로직을 재사용 가능하게 만든 함수useXXX 형태로 작성하는 것이 권장됨
useFetch, useLocalStorage 등