Combining a reducer with context(reducer와 context를 결합하기)
예를 들어 reducer로 state를 관리하는 경우에, App.js와 같이 앱 상단에 reducer 함수를 정의했는 경우, 다른 컴포넌트에서 state를 변경하려면 prop을 통해 state와 state를 변경할 수 있는 이벤트 핸들러를 명시적으로 전달해야 한다.
이때, state와 dispatch 함수를 props를 통해 전달하는 대신 context에 넣어 사용한다면 "prop drilling" 없이 모든 컴포넌트 트리에서 task를 읽고 dispatch 함수를 실행할 수 있다.
Step 1: Create the context(Context 생성하기)
useReducer는 현재 state와 state를 업데이트할 수 있는 dispatch 함수를 반환한다.
만약 트리를 통해 전달하려면, 두 개의 별개의 context를 생성해야 한다.
import { createContext } from 'react';
// 현재 state를 담을 context
export const TasksContext = createContext(null);
// state를 업데이트할 dispatch를 담을 context
export const TasksDispatchContext = createContext(null);
Step 2: Put state and dispatch into context(State와 dispatch 함수를 context에 넣기)
- useReducer를 통해 반환된 state와 dispatch를 context provider를 통해 아래 트리 전체에 전달.
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>
);
}
Step 3: Use context anywhere in the tree(트리 안에서 context 사용하기)
- context를 통해 state와 dispatch를 전달할 수 있기 때문에, props를 통해서 전달할 필요가 없다.
- state와 dispatch가 필요한 컴포넌트에서 context의 state와 dispatch 함수를 읽고 호출할 수 있다.
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>
// ...
Moving all wiring into a single file(하나의 파일로 합치기)
- 필수는 아니지만, reducer와 context를 모두 하나의 파일에 작성하면 컴포넌트들을 조금 더 정리할 수 있다.
- reducer를 같은 파일로 옮기고 provider 컴포넌트를 새로 선언한다.(이 컴포넌트는 모든 것을 하나로 묶는 역할을 하게 된다)
* 예시 코드
아래 예시 코드에서는 context와 reducer가 모두 하나의 파일에 있고, 컴포넌트는 데이터를 어디서 가져오는지가 아닌 무엇을 보여줄 것인지에 집중할 수 있다.
import { createContext, useContext, useReducer } from 'react';
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
export function useTasks() {
return useContext(TasksContext);
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}
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 }
];
* context에 사용하기 위한 useTasks 또는 useTaskDispatch와 같은 커스텀 훅을 만들 수도 있다. 커스텀 훅 안에서도 useContext 등 다른 Hook을 사용할 수 있다.
* 참고 : React 공식문서(https://react-ko.dev/learn)
'이것저것 스터디📚 > React - 공식 문서' 카테고리의 다른 글
React 공식문서 -Manipulating the DOM with Refs(ref로 DOM 조작하기) (0) | 2023.08.16 |
---|---|
React 공식문서 -Referencing Values with Refs(ref로 값 참조하기) (0) | 2023.08.15 |
React 공식문서 -Passing Data Deeply with Context(context로 데이터 깊숙이 전달하기) (0) | 2023.08.09 |
React 공식문서 -Extracting State Logic into a Reducer(State 로직을 Reducer로 추출하기) (0) | 2023.08.09 |
React 공식문서 -Preserving and Resetting State(state 보존 및 재설정) (0) | 2023.08.03 |