Redux Thunk
ㄱRedux Thunk는 Redux에서 비동기 작업을 처리하기 위한 미들웨어입니다. Redux는 기본적으로 동기적으로 상태를 관리하지만, 비동기 작업(예: API 호출, 타이머, 파일 읽기 등)을 처리할 수 있는 기능이 내장되어 있지 않습니다. Redux Thunk를 사용하면 이러한 비동기 작업을 처리할 수 있습니다.
Redux Thunk의 역할
- 비동기 액션 처리: Redux Thunk를 사용하면 액션 생성자가 함수를 반환할 수 있습니다. 이 함수는 비동기 작업을 수행하고, 필요에 따라 디스패치를 여러 번 호출하여 상태를 업데이트할 수 있습니다.
- 로직 캡슐화: 비동기 로직이나 조건부 로직을 액션 생성자 내부에 캡슐화하여, 컴포넌트가 간결해지고 비즈니스 로직이 분리됩니다.
기본 개념
Redux Thunk는 함수 형태의 액션을 디스패치할 수 있게 합니다. 이 함수는 dispatch
와 getState
를 매개변수로 받아서, 비동기 작업을 수행하고 필요한 시점에 디스패치를 호출하여 상태를 업데이트합니다.
설치
Redux Thunk를 설치하려면 다음 명령어를 사용합니다:
npm install redux-thunk
기본 사용법
- 스토어 설정: Redux Thunk를 미들웨어로 적용합니다.
// store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;
- 비동기 액션 생성자: 비동기 작업을 수행하는 액션 생성자를 작성합니다.
// actions.js
import axios from 'axios';
export const fetchTodosRequest = () => ({
type: 'FETCH_TODOS_REQUEST'
});
export const fetchTodosSuccess = (todos) => ({
type: 'FETCH_TODOS_SUCCESS',
payload: todos
});
export const fetchTodosFailure = (error) => ({
type: 'FETCH_TODOS_FAILURE',
payload: error
});
export const fetchTodos = () => {
return async (dispatch) => {
dispatch(fetchTodosRequest());
try {
const response = await axios.get('http://localhost:3001/todos');
dispatch(fetchTodosSuccess(response.data));
} catch (error) {
dispatch(fetchTodosFailure(error.message));
}
};
};
- 리듀서: 액션 타입에 따라 상태를 업데이트합니다.
// reducer.js
const initialState = {
loading: false,
todos: [],
error: ''
};
const todoReducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_TODOS_REQUEST':
return {
...state,
loading: true
};
case 'FETCH_TODOS_SUCCESS':
return {
loading: false,
todos: action.payload,
error: ''
};
case 'FETCH_TODOS_FAILURE':
return {
loading: false,
todos: [],
error: action.payload
};
default:
return state;
}
};
export default todoReducer;
- 컴포넌트에서 디스패치: 컴포넌트에서 비동기 액션을 디스패치합니다.
// ToDoContainer.jsx
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchTodos } from './actions';
import Form from './Form';
import CardList from './CardList';
const ToDoContainer = () => {
const dispatch = useDispatch();
const { loading, todos, error } = useSelector((state) => state.todos);
useEffect(() => {
dispatch(fetchTodos());
}, [dispatch]);
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
const addToDo = (newTodo) =>
setToDos((prevToDos) => prevToDos && [...prevToDos, newTodo]);
const deleteToDo = (toDoId) =>
setToDos((prevToDos) => prevToDos && prevToDos.filter((todo) => todo.id !== toDoId));
const toggleIsDone = (toDoId) =>
setToDos((prevToDos) =>
prevToDos &&
prevToDos.map((todo) =>
todo.id === toDoId ? { ...todo, isDone: !todo.isDone } : todo
)
);
const workingToDos = (todos || []).filter((todo) => !todo.isDone);
const doneToDos = (todos || []).filter((todo) => todo.isDone);
return (
<div className="top_wrapper">
<header className="my_header">
<h3>My Todo List</h3>
<p>React</p>
</header>
<Form addToDo={addToDo} />
<section className="content_section">
<CardList
title={"Working...🔥"}
toDos={workingToDos}
deleteToDo={deleteToDo}
toggleIsDone={toggleIsDone}
/>
<CardList
title={"Done...🎉"}
toDos={doneToDos}
deleteToDo={deleteToDo}
toggleIsDone={toggleIsDone}
/>
</section>
</div>
);
};
export default ToDoContainer;
결론
Redux Thunk는 비동기 작업을 처리하는 간단하고 강력한 방법을 제공합니다. 이를 통해 Redux 스토어에서 비동기 로직을 처리할 수 있으며, 비동기 작업을 수행한 후 상태를 업데이트할 수 있습니다. 이를 통해 Redux 애플리케이션에서 비동기 작업을 보다 쉽게 관리할 수 있습니다.
Redux Toolkit
Redux Toolkit을 사용하면 Redux의 보일러플레이트 코드를 줄이고, 비동기 작업을 쉽게 처리할 수 있습니다.
Redux Thunk는 Redux Toolkit에 기본적으로 포함되어 있습니다.
1. 설치
먼저 Redux Toolkit과 React Redux를 설치합니다.
npm install @reduxjs/toolkit react-redux
2. 스토어 설정
store.js
파일을 생성하고 스토어를 설정합니다.
// store.js
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './todoSlice';
const store = configureStore({
reducer: {
todos: todoReducer
}
});
export default store;
3. 슬라이스 생성
todoSlice.js
파일을 생성하고 슬라이스를 설정합니다.
// todoSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// 비동기 Thunk 액션 생성
export const fetchTodos = createAsyncThunk('todos/fetchTodos', async () => {
const response = await axios.get('http://localhost:3001/todos');
return response.data;
});
const todoSlice = createSlice({
name: 'todos',
initialState: {
items: [],
loading: false,
error: null
},
reducers: {
addToDo: (state, action) => {
state.items.push(action.payload);
},
deleteToDo: (state, action) => {
state.items = state.items.filter(todo => todo.id !== action.payload);
},
toggleIsDone: (state, action) => {
const todo = state.items.find(todo => todo.id === action.payload);
if (todo) {
todo.isDone = !todo.isDone;
}
}
},
extraReducers: (builder) => {
builder
.addCase(fetchTodos.pending, (state) => {
state.loading = true;
})
.addCase(fetchTodos.fulfilled, (state, action) => {
state.loading = false;
state.items = action.payload;
})
.addCase(fetchTodos.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
}
});
export const { addToDo, deleteToDo, toggleIsDone } = todoSlice.actions;
export default todoSlice.reducer;
4. 컴포넌트 수정
ToDoContainer.jsx
파일을 수정하여 Redux Toolkit과 연동합니다.
// ToDoContainer.jsx
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchTodos, addToDo, deleteToDo, toggleIsDone } from './todoSlice';
import Form from './Form';
import CardList from './CardList';
const ToDoContainer = () => {
const dispatch = useDispatch();
const { items: todos, loading, error } = useSelector((state) => state.todos);
useEffect(() => {
dispatch(fetchTodos());
}, [dispatch]);
const handleAddToDo = (newTodo) => {
dispatch(addToDo(newTodo));
};
const handleDeleteToDo = (toDoId) => {
dispatch(deleteToDo(toDoId));
};
const handleToggleIsDone = (toDoId) => {
dispatch(toggleIsDone(toDoId));
};
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
const workingToDos = todos.filter((todo) => !todo.isDone);
const doneToDos = todos.filter((todo) => todo.isDone);
return (
<div className="top_wrapper">
<header className="my_header">
<h3>My Todo List</h3>
<p>React</p>
</header>
<Form addToDo={handleAddToDo} />
<section className="content_section">
<CardList
title={"Working...🔥"}
toDos={workingToDos}
deleteToDo={handleDeleteToDo}
toggleIsDone={handleToggleIsDone}
/>
<CardList
title={"Done...🎉"}
toDos={doneToDos}
deleteToDo={handleDeleteToDo}
toggleIsDone={handleToggleIsDone}
/>
</section>
</div>
);
};
export default ToDoContainer;
5. Provider 설정
index.js
파일에서 Redux Provider를 설정합니다.
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Redux Toolkit을 사용하여 리액트 애플리케이션에서 비동기 작업을 처리하고, 상태를 관리할 수 있습니다. fetchTodos
Thunk를 사용하여 비동기적으로 데이터를 가져오고, 이를 상태에 반영하는 작업을 쉽게 수행할 수 있습니다.
'library' 카테고리의 다른 글
[240528 TIL] Axios (0) | 2024.05.28 |
---|---|
[240524 TIL] RTK Immer? (0) | 2024.05.24 |
[240521 TIL] JSON server (0) | 2024.05.21 |
[240520 TIL] Redux (0) | 2024.05.20 |
[240516 TIL] transient prop(styled-components) (0) | 2024.05.16 |