- styled-components를 통한 컴포넌트 스타일링 (이전 게시글)
- Context API를 사용한 전역 상태 관리 (이번 게시글!)
- [ 이전 Stae 관리 ]
: 상위 컴포넌트에서 하위 컴포넌트로 State를 전달하기 위해 props로 지정해서 계속 넘겨줘야 했다.
(거쳐가는 컴포넌트가 많을 수록 비효율적)
- [Context API 사용]
: Context를 사용해서 전역 범위로 state를 관리할 수 있기 때문에 각 컴포넌트에서 Direct Access 가능
사용하기
1단계) reducer 작성 + context 생성
- ( 전체 코드 )
<TodoContext.js> import React, { useReducer, createContext, useContext } from 'react'; const initialTodos = [ { id: 1, text: '프로젝트 생성하기', done: true }, { id: 2, text: '컴포넌트 스타일링하기', done: true }, { id: 3, text: 'Context 만들기', done: false }, { id: 4, text: '기능 구현하기', done: false } ]; function todoReducer(state, action) { switch (action.type) { case 'CREATE': return state.concat(action.todo); case 'TOGGLE': return state.map(todo => todo.id === action.id ? { ...todo, done: !todo.done } : todo ); case 'REMOVE': return state.filter(todo => todo.id !== action.id); default: throw new Error(`Unhandled action type: ${action.type}`); } } const TodoStateContext = createContext(); const TodoDispatchContext = createContext(); export function TodoProvider({ children }) { const [state, dispatch] = useReducer(todoReducer, initialTodos); return ( <TodoStateContext.Provider value={state}> <TodoDispatchContext.Provider value={dispatch}> {children} </TodoDispatchContext.Provider> </TodoStateContext.Provider> ); }
- ( reducer 작성 부분 )
<TodoContext.js> ... function todoReducer(state, action) { switch (action.type) { case 'CREATE': return state.concat(action.todo); case 'TOGGLE': return state.map(todo => todo.id === action.id ? { ...todo, done: !todo.done } : todo ); case 'REMOVE': return state.filter(todo => todo.id !== action.id); default: throw new Error(`Unhandled action type: ${action.type}`); } } ...
-각 action에 맞는 연산을 수행해서 state값 변경
-불변성을 지키면서 state를 변경하는 것이 Point!
-실제 서버와 연결하여 로직이 많아지면 별도 action파일로 분리!
- ( context 생성 및 사용 부분 )
<TodoContext.js> ... /* Context 생성 */ const TodoStateContext = createContext(); const TodoDispatchContext = createContext(); const TodoNextIdContext = createContext(); export function TodoProvider({ children }) { const [state, dispatch] = useReducer(todoReducer, initialTodos); const nextId = useRef(5); return ( /* Context 사용 */ <TodoStateContext.Provider value={state}> <TodoDispatchContext.Provider value={dispatch}> <TodoNextIdContext.Provider value={nextId}> {children} </TodoNextIdContext.Provider> </TodoDispatchContext.Provider> </TodoStateContext.Provider> ); } ...
-Context를 3개로 분리하였음 (State 용도 / Dispatch 용도 / netxId 용도)
-Context는 생성 후 .Provider 태그로 사용
-각 Context는 value로 state와 dispatch를 지정
2단계) 커스텀 Hook 만들기 + 예외처리
- ( 커스텀 미 적용 호출 방법 )
import React, { useContext } from 'react'; import { TodoStateContext, TodoDispatchContext } from '../TodoContext'; function Sample() { const state = useContext(TodoStateContext); const dispatch = useContext(TodoDispatchContext); return <div>Sample</div>; }
: useContext로 해당 Context를 직접 넣어줘야 한다
- ( 커스텀 적용 및 호출방법 )
<TodoContext.js> - 커스텀 적용 ... export function useTodoState() { const context = useContext(TodoStateContext); if (!context) { throw new Error('Cannot find TodoProvider'); } return context; } export function useTodoDispatch() { const context = useContext(TodoDispatchContext); if (!context) { throw new Error('Cannot find TodoProvider'); } return context; } export function useTodoNextId() { const context = useContext(TodoNextIdContext); if (!context) { throw new Error('Cannot find TodoProvider'); } return context; }
<커스텀 hook 호출 방법 > import React from 'react'; import { useTodoState, useTodoDispatch } from '../TodoContext'; function Sample() { const state = useTodoState(); const dispatch = useTodoDispatch(); return <div>Sample</div>; }
: 커스텀에서 이미 useContext로 Context를 넣었기 때문에
커스텀 함수 이름으로 바로 사용
3단계) Component 감싸기
<App.js> import { TodoProvider } from './TodoContext'; ... function App() { return ( <TodoProvider> <GlobalStyle /> <TodoTemplate> <TodoHead /> <TodoList /> <TodoCreate /> </TodoTemplate> </TodoProvider> ); }
: 태그들을 감싸줘야 그 내부에서 사용 가능
4단계) 컴포넌트에서 사용하기
import { useTodoState } from '../TodoContext'; import { useTodoDispatch } from '../TodoContext'; ... /* 커스텀 적용 */ -- 커스텀 한 함수로 바로 사용 가능 const todos = useTodoState(); const dispatch = useTodoDispatch(); /* 커스텀 미적용 */ -- Context자체를 useContext의 인자로 넣음 const todos = useContext(TodoStateContext); const dispatch = useContext(TodoDispatchContext);
[ 결과물 ]