250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 유니크제약조건
- BOJ
- 힙
- dfs
- 즉시로딩
- 스토어드 프로시저
- 백트래킹
- JPQL
- execute
- 지연로딩
- 연관관계
- querydsl
- 데코레이터
- exclusive lock
- 다대다
- 스프링 폼
- 동적sql
- 낙관적락
- shared lock
- fetch
- CHECK OPTION
- 연결리스트
- 비관적락
- PS
- 일대다
- eager
- FetchType
- 다대일
- SQL프로그래밍
- 이진탐색
Archives
- Today
- Total
흰 스타렉스에서 내가 내리지
상태 로직을 리듀서로 추출하기 본문
728x90
많은 이벤트 핸들러에 걸쳐 많은 상태 업데이트가 분산되는 컴포넌트는 복잡해질 수 있습니다.
이러한 경우에는 컴포넌트 외부에 모든 상태 업데이트 로직을 단일 함수인 "리듀서"로 통합할 수 있습니다.
이벤트 핸들러는 사용자 "액션"만을 지정하기 때문에 간결해집니다.
파일의 맨 아래에서 리듀서 함수는 각 액션에 대한 상태 업데이트 방식을 지정합니다!
리듀서를 사용하는 경우 상태를 설정하기 위한 이벤트 핸들러 대신 액션을 디스패치하여 "사용자가 방금 무엇을 했는지"를 지정합니다. (상태 업데이트 로직은 다른 곳에 있습니다!)
따라서 이벤트 핸들러를 통해 "작업 추가/변경/삭제" 액션을 디스패치하는 대신 "작업을 추가/변경/삭제했다"라고 명시합니다.
이는 사용자의 의도를 더 명확하게 나타냅니다.
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에 전달하는 객체를 "액션"이라고 합니다
function handleDeleteTask(taskId) {
dispatch(
// "액션" 객체:
{
type: 'deleted',
id: taskId,
}
);
}
# App.js
import { useReducer } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';
export default function TaskApp() {
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
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
});
}
return (
<>
<h1>Prague itinerary</h1>
<AddTask
onAddTask={handleAddTask}
/>
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
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);
}
}
}
let nextId = 3;
const initialTasks = [
{ id: 0, text: 'Visit Kafka Museum', done: true },
{ id: 1, text: 'Watch a puppet show', done: false },
{ id: 2, text: 'Lennon Wall pic', done: false }
];
리듀서 내부에는 switch 문을 사용하는 것이 관례입니다
# AddTask.js
import { useState } from 'react';
export default function AddTask({ onAddTask }) {
const [text, setText] = useState('');
return (
<>
<input
placeholder="Add task"
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={() => {
setText('');
onAddTask(text);
}}>Add</button>
</>
)
}
# TaskList.js
import { useState } from 'react';
export default function TaskList({
tasks,
onChangeTask,
onDeleteTask
}) {
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
<Task
task={task}
onChange={onChangeTask}
onDelete={onDeleteTask}
/>
</li>
))}
</ul>
);
}
function Task({ task, onChange, onDelete }) {
const [isEditing, setIsEditing] = useState(false);
let taskContent;
if (isEditing) {
taskContent = (
<>
<input
value={task.text}
onChange={e => {
onChange({
...task,
text: e.target.value
});
}} />
<button onClick={() => setIsEditing(false)}>
Save
</button>
</>
);
} else {
taskContent = (
<>
{task.text}
<button onClick={() => setIsEditing(true)}>
Edit
</button>
</>
);
}
return (
<label>
<input
type="checkbox"
checked={task.done}
onChange={e => {
onChange({
...task,
done: e.target.checked
});
}}
/>
{taskContent}
<button onClick={() => onDelete(task.id)}>
Delete
</button>
</label>
);
}
Immer를 사용하여 간결한 리듀서 작성하기
일반 상태 업데이트와 마찬가지로 리듀서를 더 간결하게 작성할 수 있는 Immer 라이브러리를 사용할 수 있습니다.
여기서 useImmerReducer를 사용하면 push 또는 arr\[i\] = 할당을 사용하여 상태를 직접 변경할 수 있습니다.
import { useImmerReducer } from 'use-immer';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';
function tasksReducer(draft, action) {
switch (action.type) {
case 'added': {
draft.push({
id: action.id,
text: action.text,
done: false,
});
break;
}
case 'changed': {
const index = draft.findIndex((t) => t.id === action.task.id);
draft[index] = action.task;
break;
}
case 'deleted': {
return draft.filter((t) => t.id !== action.id);
}
default: {
throw Error('Unknown action: ' + action.type);
}
}
}
export default function TaskApp() {
const [tasks, dispatch] = useImmerReducer(tasksReducer, initialTasks);
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,
});
}
return (
<>
<h1>Prague itinerary</h1>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
let nextId = 3;
const initialTasks = [
{id: 0, text: 'Visit Kafka Museum', done: true},
{id: 1, text: 'Watch a puppet show', done: false},
{id: 2, text: 'Lennon Wall pic', done: false},
];
'React' 카테고리의 다른 글
변이를 사용하지 않고 배열 업데이트하기 (0) | 2024.05.19 |
---|---|
Immer를 사용하여 간결한 업데이트 로직 작성하기 (0) | 2024.05.19 |
이벤트 핸들러 props 로 전달하기, 이벤트 전파, 이벤트 방지 (0) | 2024.05.19 |
이벤트에 응답하기 - 발생할 수 있는 버그 (0) | 2024.05.19 |
JSX 스프레드 구문을 사용하여 props 전달하기 (0) | 2024.05.18 |