리덕스 툴킷에서는 비동기 작업을 어떻게 처리할까.
CreateAsyncThunk({action type}, {action creater가 호출됐을 때 실행할 함수})
// 파라미터 2개 필요
const asyncUpFetch = createAsyncThunk(
'counterSlice/asyncUpFetch',
async () => {
const resp = await fetch('https://api.countapi.xyz/hit/opesaljkdfslkjfsadf.com/visits')
const data = await resp.json();
return data.value;
// 리턴 값으로 store의 state변경
}
)
asnyncUpFetch는 액션 크리에이터이기 때문에 타입이 있어야 한다.
액션이 실행됐을 때 처리되어야 하는 작업(서버 접속, 결과 가져오기)을 리턴하는 값을 두번째 파라미터의 함수로써 전달했다.
세 가지 상태별로 필요한 리듀서를 각각 정의한다.
const counterSlice = createSlice({
name: 'counterSlice',
initialState: {
value: 0,
status: 'Welcome',
},
reducers: {
up: (state, action) => {
state.value = state.value + action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(asyncUpFetch.pending, (state, action) => {
state.status = 'Loading';
});
builder.addCase(asyncUpFetch.fulfilled, (state, action) => {
state.value = action.payload;
state.status = 'complete';
});
builder.addCase(asyncUpFetch.rejected, (state, action) => {
state.status = 'fail';
});
},
});
동기적인 액션은 reducers (action creacter를 자동으로 생성)
비동기 작업은 extraReducers 사용 (action creater 자동 생성 X)
그냥 이 문법 자체를 외우고 사용하면 될 것 같다.
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
const asyncUpFetch = createAsyncThunk('counterSlice/asyncUpFetch', async () => {
const resp = await fetch(
'https://api.countapi.xyz/hit/opesaljkdfslkjfsadf.com/visits'
);
const data = await resp.json();
return data.value;
});
//
const counterSlice = createSlice({
name: 'counterSlice',
initialState: {
value: 0,
status: 'Welcome',
},
reducers: {
// 동기적인 액션
up: (state, action) => {
state.value = state.value + action.payload;
},
},
extraReducers: (builder) => {
// 비동기 작업
builder.addCase(asyncUpFetch.pending, (state, action) => {
state.status = 'Loading';
});
builder.addCase(asyncUpFetch.fulfilled, (state, action) => {
state.value = action.payload;
state.status = 'complete';
});
builder.addCase(asyncUpFetch.rejected, (state, action) => {
state.status = 'fail';
});
},
});
export default counterSlice;
export const { up, set } = counterSlice.actions;
export { asyncUp, asyncUpFetch };
import {configureStore} from '@reduxjs/toolkit';
import counterSlice from './counterSlice';
const store = configureStore({
reducer:{
counter:counterSlice.reducer
}
});
export default store;
import React from 'react';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
import store from './store';
import { up } from './counterSlice';
import { asyncUpFetch } from './counterSlice';
function Counter() {
const dispatch = useDispatch();
const count = useSelector((state) => {
return state.counter.value;
});
const status = useSelector((state) => {
return state.counter.status;
});
return (
<div>
<button
onClick={() => {
dispatch(up(2));
}}
>
+
</button>
<button
onClick={() => {
dispatch(asyncUpFetch());
}}
>
+ async fetch
</button>
<br />
<div>
{count} | {status}
</div>
</div>
);
}
export default function App() {
return (
<Provider store={store}>
<div>
<Counter></Counter>
</div>
</Provider>
);
}
- createAsyncThunk은 비동기작업을 처리하는 action creator를 만든다.
- action creator는 아래와 같이 3가지 상태를 갖는다.
- action creator.pending는 대기상태.
- action creator.fulfilled 는 완료 상태.
- action creator.rejected는 오류 상태.- thunk는 각각의 상태에 따른 reducer를 체계적으로 작성할 수 있도록 유도한다.
- thunk를 처리할 때는 extraReducers를 사용한다.
근데 아무리 봐도 코드의 양이.. 양 계산 잘못해서 미친듯이 불어난 미역같다.
간단한 로직이 이 정도인데 실제 개발에선 어떻게 사용될지 궁금하기도 하고 두렵기도 하고 그렇다.