티스토리 뷰
- json-server를 이용해 Thunk 함수를 통해서 API를 호출하고 서버로부터 가져온 값을 Store에 dispatch 하는 기능 구현하기
- extraReducers를 사용해 리듀서 로직 구현하기
- 비동기 통신 상태를 관리하는 로직 구현하기
extraReducers
reducers에서 바로구현되지 않는 기타 Reducer로직을 구현할 때 사용하는 기능.
보통 thunk 함수를 사용할 때 extraReducers를 사용한다.
thunk 함수 작성
- const data는 Promise를 반환한다
- axios.get() (함수)가 반환하는 Promise의 fullfilled 또는 rejected 된 것을 처리하기 위해 async/await 추가
- 요청 성공 / 실패 결과 확인을 위해 try.. catch 구문 사용
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: [],
isLoading: false,
error: null,
};
export const __getTodos = createAsyncThunk(
"todos/getTodos",
async (payload, thunkAPI) => {
try {
const data = await axios.get("http://localhost:3001/todos");
console.log(data);
} catch (error) {
console.log(error);
}
}
);
비동기 함수를 통해 가져온 데이터를 Store로 dispatch 하기
- 서버에서 데이터를 가져온 후, 그 데이터를 dispatch를 통해 Redux 스토어의 state에 전달하는 것
- fulfillWithValue는 툴킷에서 제공하는 API , Promise resolve(성공)인 경우, dispatch해주는 기능을 가진 API이다. 인자로는 payload를 넣어줄 수 있다
- Promise reject(실패) 인경우, dispatch 해주는 기능을 가진 API 인자로 어떤 값을 넣을 수 있다. 아래 코드는 catch에서 잡아주는 error 객체(페이로드 = 인자 / 인자값으로 들어가는 것이 페이로드이다 )를 넣었다.
fulfillWithValue에 페이로드를 설정하지 않는 경우
페이로드를 설정하지 않으면 액션은 dispatch 되지만, 스토어의 상태는 변경되지 않는다. 해당 액션이 디스패치되었지만, 스토어의 리듀서가 그 액션을 처리하지 않았기 때문
서버에서 받은 데이터를 스토어에 저장하기 위해서는 액션을 dispatch(전달)할 때 해당 데이터를 페이로드로 설정해줘야 한다.
rejectWithValue에 페이로드를 설정하는 경우
rejectWithValue에서 페이로드를 설정하는 것은 주로 에러 상황에서 사용된다. 이 경우에도 마찬가지로 페이로드를 설정해줘야 한다 이 페이로드는 에러 메시지나 관련된 정보를 포함하며 상태(state)에 에러를 전달하고, 에러를 처리하거나 UI에 표시할 수 있습니다.
정리
액션을 dispatch 할 때 페이로드를 설정해 줘야 해당 액션이 스토어의 상태를 변경할 수 있다. 에러 상황에서도 페이로드를 설정하여 해당 에러를 스토어의 상태에 저장하고 관리할 수 있다.
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: [],
isLoading: false,
error: null,
};
export const __getTodos = createAsyncThunk(
"todos/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);
}
}
);
리듀서 로직 구현
- Slice 내부에 있는 extraRecuders에서 pending, fulfilled, rejected에 대해 각각 어떻게 새로운 state를 반환할 것인지 구현할 수 있다
- thunkAPI.fulfillWithValue(data.data) 를 통해 [__getTodos.fulfilled]으로 디스패치가 된다. 그래서 action을 콘솔에 찍어보면 fulfillWithValue(data.data)가 보낸 액션객체를 볼 수 있다. (type과 payload가 있음)
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: [],
isLoading: false,
error: null,
};
export const __getTodos = createAsyncThunk(
"todos/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);
}
}
);
export const todosSlice = createSlice({
name: "todos",
initialState,
extraReducers: (builder) => {
builder.addCase(__getTodos.fulfilled, (state, action) => {
state.todos = action.payload; // 액션의 payload를 스토어의 state에 저장(서버에서 가져온 데이터 저장)
state.isLoading = false; // 네트워크 요청 성공상태이므로 로딩 상태를 false로 변경
state.error = null; // 에러 상태 초기
});
builder.addCase(__getTodos.pending, (state) => {
state.isLoading = true; // 요청이 보내졌으므로 로딩 상태를 true로 변경
});
builder.addCase(__getTodos.rejected, (state, action) => {
state.error = action.payload; // 에러 발생 시, catch 된 error객체를 state에 저장
state.isLoading = false; // 로딩 상태를 false로 변경
});
},
});
export const {} = todosSlice.actions;
export default todosSlice.reducer;
Store 값 조회하고, 화면에 렌더링 하기
를 이용해서 store값을 조회하고, 화면에 렌더링해
- useSelector로 initialState 가져오기(상태값, 서버에서 받은 데이터를 저장할 state)
- 각 상태에 따라 조건문을 이용해 화면에 메시지를 출력할 수 있다
- todos?. map은 메서드 체이닝으로 서버로부터 받아온 데이터가 있을 경우에만 반복한다는 것
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { __getTodos } from "./redux/modules/todosSlice";
const App = () => {
const dispatch = useDispatch();
const { isLoading, error, todos } = useSelector((state) => state.todos);
useEffect(() => {
dispatch(__getTodos());
}, [dispatch]);
if (isLoading) {
return <div>로딩 중....</div>;
}
if (error) {
return <div>{error.message}</div>;
}
return (
<div>
{todos?.map((todo) => (
<div key={todo.id}>{todo.title}</div>
))}
</div>
);
};
export default App;
'React' 카테고리의 다른 글
[리액트 React] 인증/인가(쿠키, 세션, 토큰, JWT) (0) | 2024.03.17 |
---|---|
[리액트 React] 리액트 쿼리 (React Query) (0) | 2024.03.17 |
[리액트 React] 리덕스 미들웨어, 청크 (Thunk) (0) | 2024.03.17 |
[리액트 React] 엑시오스 인터셉터(Axios interceptor) (0) | 2024.03.16 |
[리액트 React] 페치, 엑시오스 차이 (Fetch , Axios) (0) | 2024.03.16 |