[React Docs] contextAPI + reducer 결합하기

lyshine·2023년 6월 20일
0

React

목록 보기
3/6

reducer와 context를 결합하기

Reducer를 사용하면 컴포넌트의 state 업데이트 로직을 통합할 수 있다.
Context를 사용하면 다른 컴포넌트들에 정보를 전달할 수 있습니다.
⇒ Reducer와 Context를 함께 사용하여 복잡한 화면의 state를 관리할 수 있다!

  • tasks state와 dispatch 함수를 props를 통해 전달하는 대신 context에 넣어서 사용한다.
    • props drilling” 없이 TaskApp 아래의 모든 컴포넌트 트리에서 tasks를 읽고 dispatch 함수를 실행할 수 있다.

결합 방법

  1. 리듀서와 Context를 생성한다.
//1. App.js에서 리듀서 생성
//useReducer훅은 현재 tasks, 업데이트할 수 있는 dispatch 함수를 반환
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

//2. TasksContext.js에서 2가지 context 생성
import { createContext } from 'react';

export const TasksContext = createContext(null); //TasksContext는 현재 tasks 리스트를 제공
export const TasksDispatchContext = createContext(null); //TasksDispatchContext는 컴포넌트에서 action을 dispatch 하는 함수를 제공
  1. State과 dispatch 함수를 context에 넣는다.

    useReducer()를 통해 반환된 tasks와 dispatch를 받고 Provider를 통해 context 제공한다.

//App.js
import { TasksContext, TasksDispatchContext } from './TasksContext.js';

export default function TaskApp() {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
  // ...
  return (
    <TasksContext.Provider value={tasks}>
      <TasksDispatchContext.Provider value={dispatch}>
        ...
      </TasksDispatchContext.Provider>
    </TasksContext.Provider>
  );
}
  1. 트리 안에서 context를 사용한다.

    tasks 리스트 상태나 이벤트 핸들러를 prop을 통해 전달할 필요가 없어진다.

    각 컴포넌트에서 useContext를 통해 필요한 context를 읽을 수 있다.

//AddTask, TaskList props 전달 사라짐
<TasksContext.Provider value={tasks}>
  <TasksDispatchContext.Provider value={dispatch}>
    <h1>Day off in Kyoto</h1>
    <AddTask />
    <TaskList />
  </TasksDispatchContext.Provider>
</TasksContext.Provider>

----
export default function TaskList() {
  const tasks = useContext(TasksContext);
  // ...

export default function AddTask() {
  const [text, setText] = useState('');
  const dispatch = useContext(TasksDispatchContext);
  // ...
  return (
    // ...
    <button onClick={() => {
      setText('');
      dispatch({
        type: 'added',
        id: nextId++,
        text: text,
      });
    }}>Add</button>
    // ...
  • action을 불러일으키기 위해 dispatch만 있으면 된다.
  • 상태, 이벤트 핸들러 등을 props로 전달해주는 것이아닌, 각각의 컴포넌트에서 필요한 것만 가져다가 쓰면 된다.

+ reducer와 context를 모두 하나의 파일로 합치기

  1. Reducer로 state를 관리
  2. 두 context를 모두 자식 컴포넌트에 제공
  3. children을 prop으로 받기 때문에 JSX를 전달할 수 있다.
import { createContext, useReducer } from 'react';

export const TasksContext = createContext(null);
export const TasksDispatchContext = createContext(null);

export function TasksProvider({ children }) {
  const [tasks, dispatch] = useReducer(tasksReducer,initialTasks); //1

	//2&3
  return (
    <TasksContext.Provider value={tasks}> 
      <TasksDispatchContext.Provider value={dispatch}>
        {children}
      </TasksDispatchContext.Provider>
    </TasksContext.Provider>
  );
}

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [...tasks, {
        id: action.id,
        text: action.text,
        done: false
      }];
    }
    case 'changed': {
      return tasks.map(t => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter(t => t.id !== action.id);
    }
    default: {
      throw Error('Unknown action: ' + action.type);
    }
  }
}

const initialTasks = [
  { id: 0, text: 'Philosopher’s Path', done: true },
  { id: 1, text: 'Visit the temple', done: false },
  { id: 2, text: 'Drink matcha', done: false }
];
  • 더 간단하고 효율적인 코드를 만들기 위해 커스텀 훅 useTasks, useTasksDispatch을 만들어 context를 불러와 사용할 수 있다.
//TaskContext.js에서 정의
export function useTasks() {
  return useContext(TasksContext);
}

export function useTasksDispatch() {
  return useContext(TasksDispatchContext);
}

⇒ 사용할 컴포넌트에 다음과 같이 불러와 사용한다.

const tasks = useTasks();
const dispatch = useTasksDispatch();

출처(리액트 공식문서) : https://react.dev/learn/scaling-up-with-reducer-and-context

0개의 댓글