티스토리 뷰

🚀 useReducer와 useContext 복습

useReducer는 state를 업데이트 하는 로직을 분리해서 dispatch를 통해서만 state를 업데이트했었습니다.

이때 dispatch에는 action 객체를 보내게 되는데요. 이 객체에는 원하는 로직과 필요한 데이터를 담아서 보냈었습니다.

 

useContext는 전역 상태 관리를 할 수 있는 hook 함수였습니다.

빈 저장소를 만들고 이 저장소를 useContext에 넘겨주게 됩니다.

최상위 컴포넌트로 방금 만든 저장소.Provider 하고 value로 전역에서 사용하고 싶은 데이터를 넘겨주면

props로 데이터를 넘겨주지 않아도 어떤 자식 컴포넌트에서 전역으로 관리되고 있는 데이터를 사용할 수 있었습니다.

 

→ 이 두 hook 함수를 함께 사용한다면 state를 전역에서 관리하는 동시에 분리한 state를 업데이트하는 로직도 전역에서 사용할 수 있을 것입니다.

 

🚀 우선 state를 담을 저장소, state업데이트 로직을 담을 저장소를 만들자

우선 src 폴더 안에 contexts라는 폴더에서 코드를 작성했습니다.

빈 저장소를 만들기 위해서는 createContext를 이용해야 했습니다.

const TodoListContext = createContext();
const TodoDispatchContext = createContext();

이제 전역에서 사용할 저장소를 useContext를 통해 전역에서 사용하는 것이었는데요.

저는 아예 useContext를 여기서 사용해서 변수 하나로 바로 스토어를 사용하도록 export하려고 합니다.

위 방법이 아니라면 매번 useContext(저장소이름) 이런식으로 useContext를 import해야 할 것입니다.

export const useTodoListState = () => useContext(TodoListContext);
export const useTodoDispatch = () => useContext(TodoDispatchContext);

위 코드에서 TodoListContext는 state 자체를 담을 저장소이고, TodoDispatchContext는 state를 업데이트하는 로직(=reducer)를 담을 저장소입니다.

 

🚀 저장소에 담을 state 업데이트 로직 = reducer를 만들어보자

두 번째 저장소에 담을 state 업데이트 로직 즉, reducer를 만들어 봅시다.

 

➕ createAction

우리는 switch 문을 통해 들어오는 action.type에 따라 다른 로직을 실행하도록 코드를 작성했었습니다.

그런데 switch문의 case에 들어가는 것은 문자열이라 하나라도 오타가 날 경우 전혀 실행이 안 될 것입니다.

따라서 createAction이라는 함수를 선언해서 사용하려고 합니다.

 

이 함수는 action.type에 들어갈 문자열을 인자로 받으면

payload만을 받을 수 있는 함수를 리턴하게 됩니다.

즉, 처음에 action.type을 넘겨주면 무조건 받은 그 type을 가지고 있는 함수를 리턴합니다.

그러면 이제 그 변수 안에 payload를 넘겨주면 알아서 (처음에 받은 타입, 사용할 때 받은 payload)로 사용할 수 있게 되는 것입니다.

export function createAction(type) {
  return function (payload) {
    return {type, payload};
  };
}
export const ADD_TODO = createAction('ADD_TODO');
export const DELETE_TODO = createAction('DELETE_TODO');
export const UPDATE_TODO = createAction('UPDATE_TODO');

const todoReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [action.payload, ...state];
      
    case 'DELETE_TODO':
      return state.filter((todo) => todo.id !== action.payload.id);
      
    case 'UPDATE_TODO':
      const newTodo = [...state];
      const todoIndex = newTodo.findIndex((todo) => todo.id === action.payload.id);
      newTodo[todoIndex].content = action.payload.content;
      newTodo[todoIndex].state = action.payload.state;
      return newTodo;
      
    default:
      return state;
  }
};

 

🚀 전역에서 관리할 value를 담고 있는 Provider 자체를 만들어서 export 하자

우리가 위에서 빈 저장소를 만들었고, 빈 저장소에 담을 데이터도 만들어 놓았으니 이제 담아서 최상단에서 사용해주기만 하면 됩니다.

우리가 useContext를 사용할 때 최상위 컴포넌트에서 저장소를 불러와서 useContext를 사용해서 전역에서 사용할 수 있도록 하여 저장소.Provider하고 value라는 인자에 그 저장소에 담을 데이터를 넣어서 전역에서 사용했었습니다.

 

그런데 이번에는 지금 저장소가 2개이기도 하고 바로 export로 사용할 수 있도록 미리 다 만들어 놓으려고 합니다.

const TodoProvider = ({ children }) => {
  const [todoList, dispatch] = useReducer(todoReducer, initialState);
  
  return (
    <TodoListContext.Provider value={todoList}>
      <TodoDispatchContext.Provider value={dispatch}>
        {children}
      </TodoDispatchContext.Provider value={dispatch}>
    </TodoListContext.Provider value={todoList}>
  )
}

export default TodoProvider;

위 코드에서 children은 해당 TodoProvider 컴포넌트 안에 들어오는 모든 자식 컴포넌트들을 의미합니다.

즉, 안에 들어오는 모든 자식 컴포넌트들이 value를 사용할 수 있다는 말과 같습니다.

🚀 마지막으로 최상위에서 Provider를 사용해주자

 

📄 App.js 파일

function App() {
  return(
    <TodoProvider>
      //...
    </TodoProvider>
    )
}
export default App;

 

🚀 한번 사용해보자

우리가 애초에 빈 저장소를 만들 때 매번 useContext를 import해서 사용하는 게 아니라 바로 변수 하나로 사용할 수 있도록 코드를 작성했고 export도 시켜놨었습니다.

따라서 해당 변수를 import 해와서 바로 사용하면 되는데요.

const dispatch = useTodoDispatch();

return(
  //...
  dispatch(ADD_TODO(newTodo));
)

위와 같이 불러와서 dispatch안에 데이터를 담아서 전달해주면 됩니다.

그리고 dispatch에는 우리가 위에서 createAction을 통해 만들어 놓았던 함수를 통해 값을 넘겨줍니다.

728x90
LIST
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함