학습내용
- Thunk 함수
- Thunk에서 Promise 다루기
Middleware
액션이 리듀서로 전달되기 전에 다른 작업을 추가해주는 작업. 대부분 서버와의 통신을 위해 사용하며 Redux-thunk 역시 미들웨어 중 하나다.
Thunk는 객체가 아닌 함수를 Dispatch
하는데 이것을 Thunk 함수라고 한다. thunk 함수는 createAsyncThunk
라는 API를 사용해서 생성한다.
redux/modules/manageTodo.js
import { createAsyncThunk } from "@reduxjs/toolkit";
export const __addTodo = createAsyncThunk(
"addTodo",
(payload, thunkAPI) => {
setTimeout(() => {
thunkAPI.dispatch(addTodo(payload));
}, 1000);
}
);
"addTodo"
(payload, thunkAPI) => {...}
payload
: 컴포넌트에서 보내주는 payload
thunkAPI
: thunk에서 제공하는 여러 기능{...}
: 리듀서로 액션을 전달하기 전에 실행할 작업Header.jsx
import { __addTodo } from "../../redux/modules/manageTodo";
const Header = () => {
.
.
.
const addBtn = () => {
if (!date) {
alert("빠진 내용이 없나 확인해보세요.");
focusDate.current.focus();
} else if (!todo) {
alert("빠진 내용이 없나 확인해보세요.");
focusTodo.current.focus();
} else {
setDate("");
setTodo("");
dispatch(
__addTodo({
id: uuid(),
date,
todo,
isDone: false,
})
);
focusDate.current.focus();
}
};
.
.
.
}
dispatch(함수)
→ 함수 실행 → 함수 내 dispatch(객체)
json-server를 띄운 후 Thunk 함수를 통해서 API를 호출하고 서버로부터 가져온 값을 스토어에
dispatch
해보자.
redux/modules/manageTodo.js
import axios from "axios";
const initialState = {
initialList: [],
isLoading: false,
error: null,
};
서버와의 통신에서는 대부분 data
, isLoading
, error
로 상태를 관리한다.
isLoading
false
인 상태에서 서버와 통신이 시작되면 true
, 통신이 성공/실패하면 false
로 변경된다. error
null
export const __getTodos = createAsyncThunk(
"getTodos",
async (payload, thunkAPI) => {
try {
const data = await axios.get("http://localhost:3001/todos");
return thunkAPI.fulfillWithValue(data.data);
} catch (error) {
return thunkAPI.rejectWithValue(error);
}
}
);
fulfillWithValue
와 rejectWithValue
는 모두 툴킷에서 제공하는 API다.
- | fullfillWithValue | rejectWithValue |
---|---|---|
네트워크 요청 | 성공 | 실패 |
매개변수 | payload | 정해지지는 않았지만 네트워크 요청 실패시에 가져오는 값이므로 주로 error를 사용 |
const manageTodo = createSlice({
.
.
.
extraReducers: {
[__getTodos.pending]: (state) => {
state.isLoading = true;
},
[__getTodos.fulfilled]: (state, action) => {
state.isLoading = false;
state.initialList = action.payload;
},
[__getTodos.rejected]: (state, action) => {
state.isLoading = false;
state.error = action.payload;
},
},
});
pending
네트워크 요청 시작
fulfilled
네트워크 요청 성공. 서버에서 가져온 데이터 반환.
rejected
네트워크 요청 실패. catch
문의 error
객체 반환.
Map Object 표기법
상대적으로 간결한 표기법이나 자바스크립트에서만 유효하기 때문에 Builder Callback 표기법을 권장함➀
const counterReducer = createReducer(0, { increment: (state, action) => state + action.payload, decrement: (state, action) => state - action.payload })
➁
const increment = createAction('increment') const decrement = createAction('decrement') const counterReducer = createReducer(0, { [increment]: (state, action) => state + action.payload, [decrement.type]: (state, action) => state - action.payload })
Builder Callback 표기법
타입스크립트와의 호환성면에서 유리➀ 각 라인마다 빌더 메서드를 나누어 호출
const counterReducer = createReducer(initialState, (builder) => { builder.addCase(increment, (state) => {}) builder.addCase(decrement, (state) => {}) })
➁ chaining 형태
const counterReducer = createReducer(initialState, (builder) => { builder .addCase(increment, (state) => {}) .addCase(decrement, (state) => {}) })
Todolist.jsx
import { useDispatch, useSelector } from "react-redux";
import { __getTodos } from "../../redux/modules/manageTodo";
import { useEffect } from "react";
const Todolist = () => {
const { isLoading, error, initialList } = useSelector(
(state) => state.manageTodo
);
useEffect(() => {
dispatch(__getTodos());
}, [dispatch]);
.
.
.
if (isLoading) {
return <>Loading...</>;
}
if (error) {
return <>{error.message}</>;
}
return(
<>
{initialList.map((item, i) => {...}
</>
);
}
useSelector
로 store를 조회해서 화면에 렌더링하는 방식은 동일하나 상태에 따라 조건부 렌더링을 해야 한다.
📌 참조문헌