React - useContext, useReducer Hook

이소라·2022년 9월 14일
0

React

목록 보기
16/23

useContext

const value = useContext(MyContext);
  • useContextReact.createContext의 반환값인 context 객체를 인수로 받고, 현재 context value를 반환함
    • useContext의 인수는 반드시 context 객체이어야 함
    • 현재 context value는 useContext를 호출한 컴포넌트 상위의 가장 가까이 있는 <MyContext.Provider>value prop에 의해 결정됨
    • 컴포넌트 상위의 가장 가까이 있는 <MyContext.Provider>가 갱신되면, useContext Hook은 <MyContext.Provider>에 전달된 가장 최신의 context value를 가지고 리렌더링을 발생시킴
    • useContext를 사용한 컴포넌트는 context value가 변할 때 항상 리렌더링됨
      • 조상 컴포넌트가 React.memoshouldComponentUpdate를 사용했더라도, useContext를 사용한 컴포넌트는 여전히 리렌더링됨
      • 컴포넌트를 리렌더링하는 비용이 크다면, memoization을 사용하여 최적화할 수 있음

Tip

  • useContext(MyContext)는 클래스에서의 static contextType = MyContext<MyContext.Consumber와 동등함
  • useContext(MyContext)는 context를 읽고, context의 변화를 구독하게 함
  • context에 value를 제공하기 위해 트리의 상위에 <MyContext.Provider가 있어야함



useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);
  • useReducer
    • useState의 대체 Hook임
    • reducer의 type : (state, action) => newState
    • useReducer의 반환값 : useReducer 현재 state와 연관된 dispatch 메서드
      • dipatch 메서드의 identity는 안정적이고, 리렌더링할 때 바뀌지 않음
        • 그래서 useEffectuseCallback의 dependency list에서 dispatch가 생략되어도 안전함
    • 여러 개의 하위 값들이 포함된 복잡한 상태 로직을 사용하거나 다음 state가 이전 state에 의존할 때 useReducer를 사용함
    • useReducer는 깊은 업데이트 발생되는 컴포넌트에 대한 성능 최적화를 할 수 있음
      • callback 대신 dipatch를 내려줄 수 있기 때문임
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

useReducer의 초기 상태 설정

  • useReducer의 상태를 초기화하는 2가지 방법
    1. useReducer의 2번째 인수로 초기 state를 전달함
    2. useReducer의 3번째 인수로 초기 state를 설정하는 함수를 전달함
      • 초기 state를 지연 설정할 수 있음
      • 초기 state를 설정하는 함수를 reducer 함수 밖에서 선언할 수 있음
// `useReducer`의 2번째 인수로 초기 state 전달
const [state, dispatch] = useReducer(
  reducer,
  {count: initicalCount}
);
// `useReducer`의 3번째 인수로 초기 state를 설정하는 함수를 전달
const init(initialCount) {
  return {count: initialCount};
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialState, init);
  
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'reset', payload: initialCount})}>Reset</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

Bailing out of a dispatch

  • useReducer에서 반환한 값이 현재 state와 같을 때, React는 자식을 렌더링하거나 effect를 실행하지 않고 bail out함
    • React는 Object.is 비교 알고리즘을 사용하여 비교함
    • React가 bail out하기 전에 컴포넌트를 렌더링해야할 수 있음

0개의 댓글