내용
일반적으로 리액트에서 데이터를 전달하는 기본 원칙은 단방향성이다. 그 말은 부모 컴포넌트에서 자식 컴포넌트 방향으로만 데이터를 전달할 수 있다는 의미이다. 컴포넌트의 구조를 잘 설계하고 합성을 적극적으로 활용해 데이터를 계속해서 넘겨줘야 하는 상황을 안만드는 것이 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 등으로 전역 상태관리와 유사한 형태로 사용할 수도 있다
내용
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를 통해 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> ); }