React
[React] Redux Toolkit에서 비동기 처리하기
fnow
2024. 2. 5. 14:02
반응형
기존 Redux에서는 middleware를 사용하여 비동기 처리를 해야 했다. 안 그래도 복잡한 코드가 더 복잡해져 유지보수가 까다로워진다. Redux Toolkit은 간결한 코드로 비동기 처리를 쉽게 할 수 있다.
Redux와 Redux toolkit 비동기 처리 비교
구분 | Redux | Redux Toolkit |
비동기 처리 도구 | Middleware - Redux Thunk - Redux Saga |
createAsyncThunk |
액션 타입 정의 | 수동으로 정의해야 함 | 자동으로 생성됨 |
코드 구문 | Boilerplate 코드가 많음 | Redux보다 간결함 |
설치 및 설정 | 별도의 미들웨어 설치 및 설정이 필요 | Redux Toolkit 패키지만 설치하면 따로 설치할 필요 없음 |
액션 생성 | 액션 생성자에서 직접 디스패치 | createAsyncThunk에서 생성된 함수로 자동 디스패치 |
상태 업데이트 | 수동으로 상태 업데이트 | 자동으로 상태 업데이트 및 액션 타입에 따라 리듀서가 처리 |
Redux에서 Redux Thunk를 사용한 예제
const fetchUser = () => {
return async (dispatch) => {
dispatch({ type: 'FETCH_USER_REQUEST' });
try {
const response = await api.fetchUser();
dispatch({ type: 'FETCH_USER_SUCCESS', payload: response.data });
} catch (error) {
dispatch({ type: 'FETCH_USER_FAILURE', payload: error.message });
}
};
};
Redux Toolkit에서 createAsyncThunk를 사용한 예제
// createAsyncThunk를 사용한 예제
import { createAsyncThunk } from '@reduxjs/toolkit';
export const fetchUser = createAsyncThunk(
'user/fetchUser', // 리듀서이름/비동기함수이름
async (userId) => {
const response = await api.fetchUser(userId);
return response.data;
});
Redux Toolkit 주요 기능
createAsyncThunk
비동기 작업을 처리하는 데 사용되는 함수를 생성한다. createSlide 내부에서 사용하며, 해당 비동기 작업에 대한 액션을 생성한다.
import { createAsyncThunk } from '@reduxjs/toolkit';
export const fetchUser = createAsyncThunk('user/fetchUser', async (userId) => {
const response = await api.fetchUser(userId);
return response.data;
});
pending, fullfilled, rejected
createAsyncThunk로 생성된 Thunk 함수의 세 가지 액션 상태다.
- pending: 비동기 작업 진행 중
- fullfiled: 비동기 작업 성공
- rejected: 비동기 작업 실패
builder.addCase(asyncUserFetch.pending, (state, action) => {
state.status = 'loading';
})
builder.addCase(asyncUserFetch.fulfilled, (state, action) => {
state.status = 'complete';
state.userList = action.payload;
})
builder.addCase(asyncUserFetch.rejected, (state, action) => {
state.status = 'fail';
})
코드 예제
UserSlice.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"
// 비동기 action creator
export const asyncUserFetch = createAsyncThunk(
'userSlice/asyncUserFetch',
async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users")
const data = await res.json();
return data
}
)
const userSlice = createSlice({
name: 'userSlice',
initialState: {
userList: [],
status: null, // status 초기값
},
extraReducers: (builder) => {
// 비동기 작업은 action creator를 자동으로 만들지 않기 때문에 extraReducers를 사용해서 action을 생성해야 한다.
builder.addCase(asyncUserFetch.pending, (state) => {
state.status = 'Loading';
})
builder.addCase(asyncUserFetch.fulfilled, (state, action) => {
state.status = 'complete';
state.userList = action.payload;
})
builder.addCase(asyncUserFetch.rejected, (state) => {
state.status = 'fail';
})
},
})
export default userSlice;
store.js
import { configureStore } from "@reduxjs/toolkit";
import userSlice from "./userSlice";
// store 통합
const store = configureStore({
reducer: {
user: userSlice.reducer,
}
})
export default store;
App.js
import './App.css';
import { Provider } from 'react-redux';
import UserList from './components/UserList';
import store from './store';
function App() {
return (
<Provider store={store}>
<div className="App">
<UserList />
</div>
</Provider>
);
}
export default App;
UserList.js
import React, { useEffect } from 'react'
import { asyncUserFetch } from '../userSlice';
import { useDispatch, useSelector } from 'react-redux';
const UserList = () => {
const dispatch = useDispatch();
const userList = useSelector(state => state.user.userList);
const status = useSelector(state => state.user.status);
useEffect(() => {
dispatch(asyncUserFetch());
}, [dispatch]);
return (
<div>
<ul>
{
status === 'Loading' ? (
<div>Loading...</div>
) : userList && userList.map(user => (
<li key={user.id}>
<div>{user.name}</div>
</li>
))
}
</ul>
</div>
)
}
export default UserList
반응형