개요
Redux Toolkit의 createAsyncThunk는 Redux 액션 타입 문자열과 프로미스를 반환하는 콜백 함수를 받아들이는 함수입니다. 제공된 액션 타입 접두사를 기준으로 프로미스 생명주기 액션 타입을 생성하며, 프로미스 콜백을 실행하고 반환된 프로미스에 따라 생명주기 액션을 디스패치하는 thunk 액션 생성기를 반환합니다.
이는 비동기 요청 생명주기를 처리하는 표준 권장 접근 방식을 추상화합니다.
createAsyncThunk는 어떤 데이터를 가져오고 있는지, 로딩 상태를 어떻게 추적할지, 반환된 데이터를 어떻게 처리할지 알 수 없으므로 리듀서 함수를 생성하지 않습니다. 이러한 액션을 처리하는 리듀서 로직을 작성하고, 애플리케이션에 적합한 로딩 상태 및 처리 로직을 포함해야 합니다.
* Redux Toolkit의 RTK Query 데이터 페칭 API는 Redux 앱을 위한 데이터 페칭 및 캐싱 솔루션으로, 데이터 페칭을 관리하기 위해 thunks나 reducers를 작성할 필요를 없앨 수 있습니다.
사용 예시:
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'
// Thunk 생성
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
const initialState = {
entities: [],
loading: 'idle',
}
그런 다음 reducers에서 작업을 처리합니다:
const usersSlice = createSlice({
name: 'users',
initialState,
reducers: {
// 표준 reducers 로직, reducers별로 자동 생성된 action type 포함
},
extraReducers: (builder) => {
// 여기에 추가 action.type에 대한 reducers를 추가하고 필요에 따라 로딩 상태를 처리합니다.
builder.addCase(fetchUserById.fulfilled, (state, action) => {
state.entities.push(action.payload)
})
}
})
// 나중에 앱에서 필요에 따라 thunk를 dispatch합니다.
dispatch(fetchUserById(123))
매개변수
createAsyncThunk는 세 가지 매개변수를 받습니다: 문자열 액션 타입 값, payloadCreator 콜백, options object.
type
비동기 요청의 생명주기를 나타내는 추가 Redux 액션 타입 상수를 생성하는 데 사용되는 문자열입니다. 예를 들어, 'users/requestStatus'라는 타입 인수는 다음과 같은 액션 타입을 생성합니다:
- pending: 'users/requestStatus/pending'
- fulfilled: 'users/requestStatus/fulfilled'
- rejected: 'users/requestStatus/rejected'
payloadCreator
프로미스를 포함한 비동기 로직의 결과를 반환해야 하는 콜백 함수입니다. 동기적으로 값을 반환할 수도 있습니다. 오류가 발생하면 Error 인스턴스나 설명적인 오류 메시지를 포함한 값을 반환하는 거부된 프로미스를 반환하거나, thunkAPI.rejectWithValue 함수를 사용하여 거부된 값 인수를 포함한 해결된 프로미스를 반환해야 합니다.
payloadCreator 함수는 적절한 결과를 계산하기 위해 필요한 모든 로직을 포함할 수 있습니다. 이는 표준 AJAX 데이터 페칭 요청, 여러 AJAX 호출을 통해 결합된 최종 값, React Native AsyncStorage와의 상호작용 등을 포함할 수 있습니다.
payloadCreator 함수는 두 가지 인수와 함께 호출됩니다:
- arg: thunk 액션 생성자가 디스패치될 때 전달된 첫 번째 매개변수를 포함하는 단일 값. 이 값은 요청의 일부로 필요한 항목 ID와 같은 값을 전달하는 데 유용합니다. 여러 값을 전달해야 하는 경우, 객체에 함께 전달하여 thunk를 디스패치합니다.
- thunkAPI: 일반적으로 Redux thunk 함수에 전달되는 모든 매개변수와 추가 옵션을 포함하는 객체:
- dispatch: Redux 스토어 dispatch 메서드
- getState: Redux 스토어 getState 메서드
- extra: 설정 시 thunk 미들웨어에 제공된 "추가 인수"
- requestId: 이 요청 시퀀스를 식별하기 위해 자동 생성된 고유 문자열 ID 값
- signal: 다른 부분의 앱 로직이 이 요청을 취소해야 한다고 표시했는지 확인할 수 있는 AbortController.signal 객체.
- rejectWithValue(value, [meta]): action creator에서 rejected response이 정의된 payload와 메타를 함께 반환 (또는 throw)할 수 있는 유틸리티 함수.
- fulfillWithValue(value, meta): action creator에서 값을 추가하면서 fulfilledAction.meta에 추가할 수 있는 유틸리티 함수.
Options
options object 를 포함한 객체:
- condition(arg, { getState, extra }): boolean | Promise<boolean>: payload 생성기의 실행을 건너뛰기 위해 사용할 수 있는 콜백. 자세한 내용은 '실행 전 취소' 섹션을 참조하세요.
- dispatchConditionRejection: condition()이 false를 반환하면, 기본 동작은 아무 액션도 디스패치되지 않습니다. thunk가 취소될 때 "rejected" 액션을 디스패치하려면 이 플래그를 true로 설정하세요.
- idGenerator(arg): string: 요청 시퀀스를 위한 requestId를 생성할 때 사용할 함수. 기본값은 nanoid를 사용하지만, 자체 ID 생성 로직을 구현할 수 있습니다.
- serializeError(error: unknown) => any: 내부 miniSerializeError 메서드를 사용자 지정 직렬화 로직으로 교체합니다.
- getPendingMeta({ arg, requestId }, { getState, extra }): any: pendingAction.meta 필드에 병합될 객체를 생성하는 함수.
반환 값
createAsyncThunk는 표준 Redux thunk action creator 를 반환합니다. thunk 액션 생성기 함수에는 pending, fulfilled 및 rejected 경우를 위한 평범한 action creator가 중첩된 필드로 첨부됩니다.
fetchUserById 예제를 사용하면 createAsyncThunk는 네 가지 함수를 생성합니다:
- fetchUserById: 작성한 비동기 payload 콜백을 시작하는 thunk action creator.
- fetchUserById.pending: 'users/fetchByIdStatus/pending' 액션을 디스패치하는 action creator .
- fetchUserById.fulfilled: 'users/fetchByIdStatus/fulfilled' 액션을 디스패치하는 action creator .
- fetchUserById.rejected: 'users/fetchByIdStatus/rejected' 액션을 디스패치하는 action creator .
디스패치되면, thunk는 다음을 수행합니다:
- 대기 중인 액션을 디스패치합니다.
- payloadCreator 콜백을 호출하고 반환된 프로미스가 완료될 때까지 기다립니다.
- 프로미스가 완료되면:
- 프로미스가 성공적으로 해결되면, 프로미스 값을 action.payload로 하는 완료된 액션을 디스패치합니다.
- rejectWithValue(value) 반환 값으로 프로미스가 해결되면, 전달된 값을 action.payload로 하고 'Rejected'를 action.error.message로 하는 거부된 액션을 디스패치합니다.
- 프로미스가 처리되지 않은 채로 실패하면, 오류 값을 직렬화하여 action.error로 하는 거부된 액션을 디스패치합니다.
최종 디스패치된 액션(완료된 또는 거부된 액션 객체)을 포함하는 완료된 프로미스를 반환합니다.
프로미스 생명주기 액션
createAsyncThunk는 createAction을 사용하여 세 가지 Redux 액션 생성기를 생성합니다: pending, fulfilled, 및 rejected. 각 생명주기 action creator는 반환된 thunk action creator에 첨부되어 리듀서 로직이 액션 타입을 참조하고 디스패치된 액션에 응답할 수 있도록 합니다. 각 액션 객체는 고유한 requestId와 arg 값을 action.meta에 포함합니다.
액션 생성기의 시그니처
interface SerializedError {
name?: string
message?: string
code?: string
stack?: string
}
interface PendingAction<ThunkArg> {
type: string
payload: undefined
meta: {
requestId: string
arg: ThunkArg
}
}
interface FulfilledAction<ThunkArg, PromiseResult> {
type: string
payload: PromiseResult
meta: {
requestId: string
arg: ThunkArg
}
}
interface RejectedAction<ThunkArg> {
type: string
payload: undefined
error: SerializedError | any
meta: {
requestId: string
arg: ThunkArg
aborted: boolean
condition: boolean
}
}
interface RejectedWithValueAction<ThunkArg, RejectedValue> {
type: string
payload: RejectedValue
error: { message: 'Rejected' }
meta: {
requestId: string
arg: ThunkArg
aborted: boolean
condition: boolean
}
}
Thunk 결과 처리
unwrapResult
.unwrap() 메서드를 사용하여 페이로드를 추출하거나 디스패치된 thunk 에서 오류를 발생시킵니다:
dispatch(fetchUserById(userId))
.unwrap()
.then((result) => {
// 액션이 성공적으로 완료된 경우의 로직
})
.catch((error) => {
// 오류를 처리하는 로직
})
취소
Thunks는 실행 전 또는 실행 중에 취소할 수 있습니다:
- 실행 전: 옵션에서 condition callback 을 사용합니다.
- 실행 중: 디스패치가 반환한 프라미스에서 abort 메서드를 사용합니다.
const promise = dispatch(fetchUserById(userId));
promise.abort();
예시
로딩 상태의 기본 Thunk
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { userAPI } from './userAPI';
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId);
return response.data;
}
);
const usersSlice = createSlice({
name: 'users',
initialState: { entities: [], loading: 'idle' },
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchUserById.fulfilled, (state, action) => {
state.entities.push(action.payload);
});
}
});
export default usersSlice.reducer;
Handling Validation Errors( 유효성 검사 오류 처리 )
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { userAPI } from './userAPI';
const updateUser = createAsyncThunk(
'users/update',
async (userData, { rejectWithValue }) => {
try {
const { id, ...fields } = userData;
const response = await userAPI.updateById(id, fields);
return response.data.user;
} catch (err) {
if (err.response) {
return rejectWithValue(err.response.data);
}
throw err;
}
}
);
const usersSlice = createSlice({
name: 'users',
initialState: { entities: {}, error: null },
reducers: {},
extraReducers: (builder) => {
builder.addCase(updateUser.fulfilled, (state, action) => {
state.entities[action.payload.id] = action.payload;
});
builder.addCase(updateUser.rejected, (state, action) => {
if (action.payload) {
state.error = action.payload.errorMessage;
} else {
state.error = action.error.message;
}
});
}
});
export default usersSlice.reducer;
Thunk를 활용한 createAsyncThunk 사용법 요약
createAsyncThunk를 사용하면 Redux 애플리케이션에서 비동기 로직을 단순화할 수 있습니다. 이를 통해 비동기 요청을 효율적으로 관리하고, 에러 처리 및 상태 관리를 개선할 수 있습니다.
'TIL' 카테고리의 다른 글
TIL - React와 Redux를 사용한 메모 앱 구현 (0) | 2024.05.24 |
---|---|
TIL - 리액트로 가계부 만들기 - 3 (0) | 2024.05.23 |
TIL - 리액트로 가계부 만들기 - 2 (0) | 2024.05.22 |
TIL - 리액트로 가계부 만들기 - 1 (0) | 2024.05.21 |
TIL - [React] 리렌더링 동작을 제어하는 역할 useEffect, useRef (0) | 2024.05.20 |