[Library] context + reducer

Logun·2023년 9월 4일
0

Library

목록 보기
4/5
post-thumbnail

✅ Context Api


  • 내용

    일반적으로 리액트에서 데이터를 전달하는 기본 원칙은 단방향성이다. 그 말은 부모 컴포넌트에서 자식 컴포넌트 방향으로만 데이터를 전달할 수 있다는 의미이다. 컴포넌트의 구조를 잘 설계하고 합성을 적극적으로 활용해 데이터를 계속해서 넘겨줘야 하는 상황을 안만드는 것이 1옵션이지만, 해당 방법으로 해결이 안될 때는 Context API를 사용할 수 있다.

  • 사용 방법

    • createContext
      const UserContext = createContext(null);

    • Provider
    const UserContext = createContext(null);
    const user = {name: "yeonuk"};
    
    <UserContext.Provider value={user}>
    	<Child />
    </UserContext.Provider>
    • useContext
    const UserContext = createContext(null);
    
    const user = {name: "yeonuk"};
    
    <UserContext.Provider value={user}>
    	<Child />
    </UserContext.Provider>
    
    function Child() {
    	const user = useContext(UserContext);
    	return <h1>{user.name}</h1>
    }
  • 주의사항

    • context API는 전역 상태 관리 X

    • 여러 컴포넌트들에게 동일한 값을 접근할수도록 만들어주는 API

    • Context + useState/useReducer 등으로 전역 상태관리와 유사한 형태로 사용할 수도 있다

✅ reducer


  • 내용

    useState의 대체 함수입니다. (state, action) => newState의 형태로 reducer를 받고 dispatch 메서드와 짝의 형태로 현재 state를 반환한다. 다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우나 다음 state가 이전 state에 의존적인 경우에 보통 useState보다 useReducer를 선호한다.

  • useReducer 사용법

    • reducer 로직의 세팅
      => 받아온 action에 따라서 state 변경 후 리턴 해주는 로직이다.
    function reducer(state, action) {
      switch (action.type) {
        case "INCREMENT":
          return state.count < action.max
            ? { count: state.count + action.step }
            : state;
        case "DECREMENT":
          return state.count > action.min
            ? { count: state.count - action.step }
            : state;
        case "RESET":
          return initialState;
        case "RANDOM":
          return {
            count:
              Math.floor(Math.random() * (action.max - action.min)) + action.min,
          };
        default:
          throw new Error("Unsupported action type:", action.type);
      }
    }
    • return 받은 로직의 사용
      => 초기값을 설정해서 넣어주고, 위에서 설정한 로직을 useReducer안으로 넣어준다.
    const initialState = { count: 0 };
    function Counter({ step = 1, min = 0, max = 10 }) {
      const [state, dispatch] = useReducer(reducer, initialState);
    
      return (
        <>
          <p>
            단계: {step}, 최소: {min}, 최대: {max}
          </p>
          <h2>{state.count}</h2>
          <button onClick={() => dispatch({ type: "INCREMENT", step, max })}>
            증가
          </button>
          <button onClick={() => dispatch({ type: "DECREMENT", step, min })}>
            감소
          </button>
          <button onClick={() => dispatch({ type: "RANDOM", min, max })}>
            무작위
          </button>
          <button onClick={() => dispatch({ type: "RESET" })}>초기화</button>
        </>
      );
    }
  • 추가 사항

    • 초기화 지연
      => init 함수는 선택적으로 제공할 수 있으며, 제공하지 않으면 initialCount 값이 초기 상태로 사용된다. 하지만 init 함수를 제공하면 더 복잡한 초기 상태를 만들거나 계산할 수 있다.
    function init(initialCount) {
      return {count: initialCount};
    }
    
    function Counter({initialCount}) {
      const [state, dispatch] = useReducer(reducer, initialCount, init);
    }

✅ context와 reducer


  • 내용

    모든 레벨의 컴포넌트 트리를 통해 콜백을 수동으로 전달하는 것을 좋은 방법이 아니다. 큰 컴포넌트 트리에서 권장되는 대안은 context를 통해 useReducer에서 dispatch 함수 컴포넌트를 전달하는 것이다.

  • 사용 방법

    const TodosDispatch = React.createContext(null);
    
    function TodosApp() {
      // 주의: `dispatch`는 다시 렌더링 간에 변경되지 않습니다
      const [todos, dispatch] = useReducer(todosReducer);
    
      return (
        <TodosDispatch.Provider value={dispatch}>
          <DeepTree todos={todos} />
        </TodosDispatch.Provider>
      );
    }
profile
로건의 개발이야기

0개의 댓글