You will learn
- reducer ํจ์๋ ๋ฌด์์ธ์ง
useState๋ฅผuseReducer๋ก ๋ฆฌํฉํ ๋ง ํ๋๋ฒ- ์ธ์ reducer๋ฅผ ์ฌ์ฉํด์ผ ํ๋์ง
- ์ข์ reducer๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ
state ๋ก์ง์ Reducer๋ก ํตํฉํ๊ธฐ
์ปดํฌ๋ํธ์ state๋ฅผ ์ ๋ฐ์ดํธํ๋ ๋ก์ง์ด ์ฌ๋ฌ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๋ถ์ฐ๋์ด ์๋ ๊ฒฝ์ฐ ๊ด๋ฆฌํ๊ธฐ์ ๋๋ฌด ๋ณต์กํ ์ ์๋ค. ์ด๋ฐ ๊ฒฝ์ฐ state ์ ๋ฐ์ดํธ ๋ก์ง์ ์ปดํฌ๋ํธ ๋ฐ์ ํ๋์ ํจ์๋ก ๋ชจ๋ ๋ชจ์ ์ฌ์ฉํ ์ ์๋๋ฐ, ์ด ํจ์๋ฅผ reducer๋ผ๊ณ ๋ถ๋ฅธ๋ค.
์ปดํฌ๋ํธ์ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ๋ฉด์ ์ปดํฌ๋ํธ์ ๊ฐ state๋ค์ด ์ด๋ป๊ฒ ์ ๋ฐ์ดํธ ๋๋์ง ํ๋์ ํ์ ํ๊ธฐ๊ฐ ์ด๋ ค์ ์ง๋ค.
์๋ฅผ๋ค์ด TaskApp ์ปดํฌ๋ํธ์์ task state๋ฅผ ์
๋ฐ์ดํธ ํ๋ ๋ก์ง์ด ์๋ค๊ณ ๊ฐ์ ํด๋ณด์ :
- ํ ์ผ์ ์ถ๊ฐํ๋
handleAddTask - ํ ์ผ์ ์์ ํ๋
handleChangeTask - ํ ์ผ์ ์ ๊ฑฐํ๋
handleDeleteTask
์ด ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ค์ setTask๋ผ๋ state ์
๋ฐ์ดํธ ํจ์๋ฅผ ๋ด๋ถ์ ์ผ๋ก ํธ์ถํ ๊ฒ์ด๋ค. ์ปดํฌ๋ํธ๊ฐ ์ปค์ง์๋ก ์ด๋ฌํ ๋ก์ง์ ์ฝ๋๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ด๋ ค์์ง๋ค.
์ด์ reducer๋ฅผ ๋์ ํ๋ฉด ์ด๋ฐ ๋ฌธ์ ๋ฅผ ์๋ฐฉํ๊ณ ํด๊ฒฐํ ์ ์๋๋ฐ, ์๋์ ๊ฐ์ ๊ณผ์ ์ ํตํด ๋ชจ๋ state ๋ณ๊ฒฝ ๋ก์ง์ reducer๋ผ๋ ํ๋์ ์ฝ๋๋ก ๋ญ์น ์ ์๋ค.
- Move : state๋ฅผ ์ค์ (set)ํ๋ ๋ฐฉ์์์ action์ ์ ๋ฌ(dispatch)ํ๋ ๋ฐฉ์์ผ๋ก ์ ํ
- Write : reducer ํจ์๋ฅผ ์์ฑ
- Use : ์ปดํฌ๋ํธ์์ reducer๋ฅผ ์ฌ์ฉ
1. Move : set state์์ action dispatch๋ก ์ ํํ๊ธฐ
ํ์ฌ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ค์ ์ด๋ ๊ฒ ์ง์ ์ ์ผ๋ก ์ด๋ป๊ฒ ํ๋ํด์ผ ํ๋์ง ํน์ ํ๊ณ ์๋ค. ์ฆ state๋ฅผ ์ค์ (set)ํ๊ณ ์๋ ๊ฒ์ด๋ค.
function handleAddTask(text) {
setTasks([...tasks, {
id: nextId++,
text: text,
done: false
}]);
}
function handleChangeTask(task) {
setTasks(tasks.map(t => {
if (t.id === task.id) {
return task;
} else {
return t;
}
}));
}
function handleDeleteTask(taskId) {
setTasks(
tasks.filter(t => t.id !== taskId)
);
}
Reducer๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ ์ด์ ์กฐ๊ธ ๋ค๋ฅด๋ค. ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ์ง์ state๋ฅผ ์ค์ ํ๋ ๋์ , ์ฌ์ฉ์๊ฐ ๋ฐฉ๊ธ ์ด๋ค ํ๋์ ํ๋์ง๋ฅผ ํน์ ํ๊ณ ์ด์ ๋ง๋ action์ ๋ณด๋ธ๋ค(dispatch).
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task,
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId,
});
}
dispatch ํจ์ ์์ ๊ฐ์ฒด๋ฅผ action ์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค. action์ ์ผ๋ฐ์ ์ธ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด๋ผ์ ํ์์ ์ธ ํ๋์ ๊ฐ์ ๊ท์น์ ์๋ค. ํ์ง๋ง ๊ธฐ๋ฅ์ ์ ๋๋ก ์ฌ์ฉํ๊ธฐ ์ํด์ type ๊ณผ ๊ฐ์ ํ๋๋ฅผ ํตํด ์ ์ด๋ ์ด๋ค ํ๋์ ํ๋์ง๋ฅผ ๊ตฌ๋ถํ ์ ์๋ ์ต์ํ์ ์ ๋ณด๊ฐ ํ์ํ๋ค.
2. Write : reducer ํจ์ ์์ฑํ๊ธฐ
์ด์ state ๋ก์ง์ ๋ชจ์๋ reducer ํจ์๋ฅผ ์์ฑํด์ผ ํ๋ค. reducer ํจ์๋ ๋ ๊ฐ์ ์ธ์๋ฅผ ๋ฐ๋๋ฐ, ํ์ฌ state์ action ๊ฐ์ฒด์ด๋ค.
function yourReducer(state, action) {
// return next state for React to set
}
๋ฆฌ์กํธ๋ state๋ฅผ reducer๊ฐ ๋ฐํํ๋ ๊ฒ์ผ๋ก ์ค์ (set)ํ ๊ฒ์ด๋ค.
์์ ์์์ ๋ง๋ reducer๋ฅผ ์์ฑํ๋ฉด ์ด๋ ๊ฒ ๋๋ค :
function tasksReducer(tasks, action) {
if (action.type === 'added') {
return [
...tasks,
{
id: action.id,
text: action.text,
done: false,
},
];
} else if (action.type === 'changed') {
return tasks.map((t) => {
if (t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
} else if (action.type === 'deleted') {
return tasks.filter((t) => t.id !== action.id);
} else {
throw Error('Unknown action: ' + action.type);
}
}
์ธ์๋ก ์ ๋ฌ๋ฐ์ state์ธ tasks๋ฅผ ๋ฐ์ ์ ํด์ง ๋ก์ง์ ์ํํ๊ณ ์๋ก์ด tasks๋ฅผ ๋ฐํํ๋ ๊ฒ์ด๋ค.
3. Use : ์ปดํฌ๋ํธ์์ reducer๋ฅผ ์ฌ์ฉํ๊ธฐ
์๋ useState ํ
์ ์ฌ์ฉํ๋ฉด ์ฝ๋๋ ์ด๋ด๊ฒ์ด๋ค :
const [tasks, setTasks] = useState(initialTasks);
๊ทธ๋ฆฌ๊ณ ์ด setTasks๋ฅผ ๊ฐ ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ํธ์ถํ์ฌ state๋ฅผ ๋ณ๊ฒฝํ๋๊ฒ ๊ธฐ์กด์ ๋ฐฉ์์ด์๋ค.
useReducer ํ
์ ์ฌ์ฉํ๋ฉด ์ฝ๋๋ ์ด๋ ๊ฒ ๋๋ค :
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
setTasks ํจ์ ๋์ dispatch ํจ์๋ฅผ ๋ฐํํ๊ณ , ์ด๊ฑธ ์ฌ์ฉํ์ฌ reducer์๊ฒ state ๋ณ๊ฒฝ์ ์์ธํ ๋ด์ฉ์ ์ ๋ฌํ๋ค.
useState์ useReducer ๋น๊ต
- ์ฝ๋๋ :
useState๋ฅผ ํตํด state๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒฝ์ฐ, ์ผ์ผ์ดsetState๋ก state ๋ณ๊ฒฝ ๋ด์ฉ์ ์ง์ ์์ฑํด์ผํ๋๋ฐ, ๊ทธ๋์ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ข ๋ฅ๊ฐ ๋ง์์๋ก ์ฝ๋๊ฐ ๋์ด๋๋ค. ํ์ง๋งuseReducer๋ก state๋ฅผ ๊ด๋ฆฌํ๋ฉด, ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ๋์ด๋๋ ๋ง์ ์ฝ๋๊ฐ ์๋ก ์์ฑ๋ ํ์๊ฐ ์๋ค. - ๊ฐ๋
์ฑ :
useReducer๋ ์ด๋ป๊ฒ์ ๋ฌด์์ ์ฝ๋๋ฅผ ๋ถ๋ฆฌํ๊ฒ ํด์ฃผ์ด ์ปดํฌ๋ํธ์ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ๋งค์ฐ ๋์ฌ์ค ์ ์๋ค. - ๋๋ฒ๊น
:
useState์ฌ์ฉ์ค ๋ฒ๊ทธ๋ฅผ ๋ฐ๊ฒฌํ๋ค๋ฉด, ์ข ์ข ์ ํํ ์ด๋์, ์ state ๋ฅผ ์๋ชป ์ค์ (set)ํ๋์ง ์ฐพ๊ธฐ ์ด๋ ค์ธ ์ ์๋ค. ํ์ง๋ง reducer ๋ฅผ ์ฌ์ฉํด ๋ชจ๋ state ๋ณ๊ฒฝ์ ํ๊ณณ์ ๋ชจ์๋๋ค๋ฉด, reducer ํจ์ ์์ type๊ณผ ์ ๋ฌ ๋ฐ์ state๋ฅผ ์ถ๋ ฅํด๋ณด๋ ๋ฑ์ ๊ฐ๋จํ ์ฝ๋๋ง์ผ๋ก ๋ฒ๊ทธ๋ฅผ ์ถ์ ํ๊ธฐ ์ฌ์์ง๋ค. - ํ ์คํ : reducer๋ ์ปดํฌ๋ํธ์ ์์กดํ์ง ์๋ ์์ ํจ์๋ค. ๊ทธ๋์ state ๋ณ๊ฒฝ ๋ก์ง์ด ๋ณต์กํ์ฌ ๋ฐ๋ก ํ ์คํธ๋ฅผ ์งํํ๊ณ ์ถ์ ๋ ๋ ๋ฆฝ์ ์ผ๋ก ๊ฒ์ฆํ ์ ์๋ค.
reducer ์์ฑ ๊ท์น
- ์์ํด์ผ ํ๋ค. reducer๋ ์ด์จ๋ state ๋ณ๊ฒฝ ํจ์์ด๊ณ , ๊ทธ๋์ ๋ ๋๋ง ์ค์ ์คํ๋๋ค. ๋ ๋๋ง ๋จ๊ณ์์ ์คํ๋๋ ๋ชจ๋ ์ฝ๋๋ ์์ํด์ผ ํ๋ค๋ ์์น์ ๋ฐ๋ผ์ผ ํ๋ค.
- ๋ชจ๋ action์ ๋จ ํ๋์ ์ฌ์ฉ์ ํ๋์ ์ค๋ช ํด์ผ ํ๋ค. ์ฌ์ง์ด ์ฌ๋ฌ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ์๋๋ผ๋. ์๋ฅผ๋ค์ด ์ฌ์ฉ์๊ฐ "Reset" ๋ฒํผ์ ๋๋ฅธ๋ค๋ฉด, ๊ทธ๊ฒ ์ฌ๋ฌ๊ฐ์ ํ๋์ ๊ฐ์ ๋ณ๊ฒฝํ๋๋ผ๋ ํ๋์ action ์ด์ด์ผ ํ๋ค.
์์ ๋ฌธ์
4. useReducerํ
์ง์ ๊ตฌํํ๊ธฐ
//App.js
export default function Messenger() {
const [state, dispatch] = useReducer(messengerReducer, initialState);
const message = state.messages[state.selectedId];
const contact = contacts.find((c) => c.id === state.selectedId);
return (
<div>
<ContactList
contacts={contacts}
selectedId={state.selectedId}
dispatch={dispatch}
/>
<Chat
key={contact.id}
message={message}
contact={contact}
dispatch={dispatch}
/>
</div>
);
}
const contacts = [
{id: 0, name: 'Taylor', email: 'taylor@mail.com'},
{id: 1, name: 'Alice', email: 'alice@mail.com'},
{id: 2, name: 'Bob', email: 'bob@mail.com'},
];
//messengerReducer.js
export const initialState = {
selectedId: 0,
messages: {
0: 'Hello, Taylor',
1: 'Hello, Alice',
2: 'Hello, Bob',
},
};
export function messengerReducer(state, action) {
switch (action.type) {
case 'changed_selection': {
return {
...state,
selectedId: action.contactId,
};
}
case 'edited_message': {
return {
...state,
messages: {
...state.messages,
[state.selectedId]: action.message,
},
};
}
case 'sent_message': {
return {
...state,
messages: {
...state.messages,
[state.selectedId]: '',
},
};
}
default: {
throw Error('Unknown action: ' + action.type);
}
}
}
//MyReact.js
import { useState } from 'react';
export function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
// ???
return [state, dispatch];
}
์ฌ๊ธฐ์ MyReact.js ํ์ผ์ ์์ฑํด useReducer ํ
์ ์ง์ ๊ตฌํํด์ผ ํ๋ค.
์ผ๋จ useReducer๋ state์ dispatch๋ฅผ ๋ฐํํ๋ค. state๋ ์ด๋ฏธ ์์ฑ๋์ด ์์ผ๋ ๋๋จธ์ง ๋ฐํ๊ฐ์ธ dispatch ํจ์๋ฅผ ์์ฑํด์ ์์ฑํด์ผ ํ๋ค.
dispatch๋action๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
//MyReact.js
import { useState } from 'react';
export function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action){
//...
}
return [state, dispatch];
}
reducer๋งค๊ฐ๋ณ์๋ฅผ ํตํดmessengerReducer๋ฅผ ์ฃผ์ ๋ฐ์๋๋ฐ,messengerReducer๋action์ ์ข ๋ฅ์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ์ฌ ๋ฐํํ๋ค.
//MyReact.js
import { useState } from 'react';
export function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action){
const temp = reducer(state,action);
//...
}
return [state, dispatch];
}
- temp๋ ์
๋ฐ์ดํธ๋
state๋ค. ์ด์ ์ด๊ฑธsetState()ํจ์๋ฅผ ํธ์ถํด state๋ฅผ ์ ๋ฐ์ดํธํ๊ณ ๋ฆฌ๋ ๋๋ฅผ ์งํํด์ผ ํ๋ค.
//MyReact.js
import { useState } from 'react';
export function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action){
const temp = reducer(state,action);
setState(temp);
}
return [state, dispatch];
}

