티스토리 뷰

  • 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;

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함