aotoyae

[React] Redux Thunk App 본문

React

[React] Redux Thunk App

aotoyae 2024. 2. 20. 00:31

 

 

2024.02.19 - [React] - [React] Redux Thunk : 미들웨어

 

[React] Redux Thunk : 미들웨어

💡 미들웨어에 대해 알아보자. 미들웨어가 없을 때 ➡️ 액션 - 리듀서 - 스토어 리덕스에서 dispatch 를 하면 action 이 리듀서로 전달되고, 리듀서는 새로운 state 를 반환한다. 여기서 미들웨어를

aotoyae.tistory.com

 

 

💡 thunk 함수를 한번 더 구현해보자!

  1. thunk 함수 구현
  2. 리듀서 로직 구현 : reducers ➡️ extraReducers
    • 서버 통신 : 100% 성공 ❌ (불확실)
    • 지금까지의 redux state - todos, counter
    • 앞으로의 state - isLoading, is Error, Data
  3. 기능 확인 : network or devTools
  4. Store 의 값을 조회, 화면에 렌더링

 

db.json

{
  "todos": [
    {
      "id": 1,
      "title": "hello world!"
    },
    {
      "id": 2,
      "title": "hello react!"
    }
  ]
}

 

todosSlice.js

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";

const initialState = {
  todos: [],
  isLoading: false, // 세 state 값을 넣어줌
  isError: false,
  error: null,
};

export const __getTodos = createAsyncThunk(
  "getTodos", // 이름
  async (payload, thunkAPI) => { // 비동기 함수
    try {
      const response = await axios.get("http://localhost:4002/todos");
      console.log("response", response);
    } catch (error) {
      console.log("error", error);
    }
  }
);

export const todosSlice = createSlice({
  name: "todos",
  initialState,
  // reducers: {},
  // extraReducers: {},
});

export const {} = todosSlice.actions;
export default todosSlice.reducer;

 

App.js

import "./App.css";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { __getTodos } from "./modules/todosSlice";

function App() {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(__getTodos());
  }, [dispatch]);

  return (
    <div>
      <h2>Thunk App</h2>
    </div>
  );
}

export default App;

 

👀 서버의 데이터(json-server 이지만)를 잘 가져오고 있다!

 

🤤 그럼 이제 toolkit 에서 제공하는 API 를 이용해 가져온 데이터를 store 에 넣어보자.

thunkAPI.fulfillWithValue()  - 성공 시 사용할 API

Promise ➡️ reslove(네트워크 요청 성공한 경우) dispatch 해주는 기능을 가진 API

 

thunkAPI.rejectWithValue() -오류 시 사용할 API

Promise ➡️ reject(네트워크 요청 실패한 경우) dispatch 해주는 기능을 가진 API

 

 

todosSlice.js

export const __getTodos = createAsyncThunk(
  "getTodos",
  async (payload, thunkAPI) => {
    try {
      const response = await axios.get("http://localhost:4002/todos"); // 여기서 오류가 나면 catch 로 간다.
      return thunkAPI.fulfillWithValue(response.data); // response.data 를 들고 dispatch ~
    } catch (error) {
      return thunkAPI.rejectWithValue(error);
    }
  }
);

export const todosSlice = createSlice({
  name: "todos",
  initialState,
  // reducers: {},
  extraReducers: (builder) => { // dispatch 받아서 ~~
    builder.addCase(__getTodos.pending, (state, action) => { // pending 으로 찾아감
      console.log("pending", action);
    });
    builder.addCase(__getTodos.fulfilled, (state, action) => { // fulfilled 로 찾아감
      console.log("fulfilled", action);
    });
    builder.addCase(__getTodos.rejected, (state, action) => { // rejected 로 찾아감
      console.log("rejected", action);
    });
  },
});

 

제 기능을 하도록 바꿔보자!

export const todosSlice = createSlice({
  name: "todos",
  initialState,
  // reducers: {},
  extraReducers: (builder) => {
    builder.addCase(__getTodos.pending, (state) => {
      state.isLoading = true; // 가져오는(진행) 중이니 로딩이 true
      state.isError = false; // 가져오는(진행) 중이니 에러가 false
    });
    builder.addCase(__getTodos.fulfilled, (state, action) => {
      state.isLoading = false; // 성공한 상태이니 로딩이 false
      state.isError = false; // 성공한 상태이니 에러가 false
      state.todos = action.payload; // 성공했으니 state.todos 에 가져온 payload 를 넣는다.
    });
    builder.addCase(__getTodos.rejected, (state, action) => {
      state.isLoading = false; // 실패한 상태이니 로딩이 false
      state.isError = true; // 실패한 상태이니 에러가 true
      state.error = action.payload; // 실패했으니 state.error 에 가져온 payload 를 넣는다.
    });
  },
});

 

App.jsx

import "./App.css";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { __getTodos } from "./modules/todosSlice";

function App() {
  const dispatch = useDispatch();
  const { isLoading, error, todos } = useSelector((state) => state.todos);

  useEffect(() => {
    dispatch(__getTodos());
  }, [dispatch]);

  if (isLoading) { // 로딩 중이니 젤 위에 위치, 아래 로직이 읽힐(렌더링 될) 필요 없음
    return <div>Loding...</div>;
  }

  if (error) { // 에러이니 아래 성공 로직이 읽힐(렌더링 될) 필요 없음
    return <div>{error.message}</div>;
  }

  return (
    <div>
      <h2>Thunk App</h2>
      {todos.map((todo) => {
        return <h3 key={todo.id}>{todo.title}</h3>;
      })}
    </div>
  );
}

export default App;

 

👀 로딩 화면이 잠깐 뜬 뒤, todos 를 잘 가져온 화면을 볼 수 있다!