ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Redux 미들웨어(Thunk)
    React 2022. 12. 20. 14:37

    미들웨어란?

    • dispatch를 해서 action이 리듀서로 전달되고, 리듀서는 새로운 state를 반환한다.
    • 미들웨어를 사용하면 이 과정 사이에 하고싶은 작업을 넣을 수 있다.
    • 예를 들어, counter 프로그램에서 + 더하기 버튼을 클릭했을 때 바로 +1을 하지 않고, 3초를 기다렸다가 +1이 되도록 구현할 수 있다.

    미들웨어를 사용하는 이유

    • 서버와의 통신을 위해서 사용하는 것이 대부분

    Thunk란?

    • 가장 많이 사용하고 있는 미들웨어 중 하나.
    • dispatch를 할 때 객체가 아닌 "함수"를 dispatch 할 수 있게 해준다.
    • 즉, dispatch(객체) 가 아니라 dispatch(함수)를 할 수 있게 된다.
    • 그래서 중간에 우리가 하고자 하는 작업을 함수를 통해 넣을 수 있고, 그것이 중간에 실행 된다.
    dispatch(함수) → 함수실행 → 함수안에서 dispatch(객체)

     

     

    Thunk 사용하기

    // src/redux/modules/counterSlice.js
    
    import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
    
    export const __addNumber = createAsyncThunk(
    	// 첫번째 인자 : action value
      "addNumber", 
    	// 두번째 인자 : 콜백함수 
      (payload, thunkAPI) => {
        setTimeout(() => {
          thunkAPI.dispatch(addNumber(payload));
        }, 3000);
      }
    );
    
    const initialState = {
      number: 0,
    };
    
    const counterSlice = createSlice({
      name: "counter",
      initialState,
      reducers: {
        addNumber: (state, action) => {
          state.number = state.number + action.payload;
        },
    
        minusNumber: (state, action) => {
          state.number = state.number - action.payload;
        },
      },
    });
    
    
    export const { addNumber, minusNumber } = counterSlice.actions;
    export default counterSlice.reducer;
    // src/App.jsx
    
    import React from 'react';
    import {useState} from 'react';
    import {useDispatch, useSelector} from 'react-redux';
    import {minusNumber, __addNumber} from './redux/modules/counterSlice';
    
    const App = () => {
      const dispatch = useDispatch();
      const [number, setNumber] = useState(0);
      const globalNumber = useSelector((state) => state.counter.number);
    
      const onChangeHandler = (evnet) => {
        const {value} = evnet.target;
        setNumber(+value);
      };
    
      // thunk 함수를 디스패치한다. payload는 thunk함수에 넣어주면,
      // 리덕스 모듈에서 payload로 받을 수 있다.
      const onClickAddNumberHandler = () => {
        dispatch(__addNumber(number));
      };
    
      const onClickMinusNumberHandler = () => {
        dispatch(minusNumber(number));
      };
    
      return (
        <div>
          <div>{globalNumber}</div>
          <input type='number' onChange={onChangeHandler} />
          <button onClick={onClickAddNumberHandler}>더하기</button>
          <button onClick={onClickMinusNumberHandler}>빼기</button>
        </div>
      );
    };
    
    export default App;

     

    이렇게 작성하면 1을 넣고 더하기를 눌렀을 때 3초 뒤 실행되는 것을 볼 수 있다.

     

     

     

    Thunk에서 Promise 다루기

    • json-server를 띄우고 Thunk 함수를 통해서 API를 호출하고 서버로부터 가져온 값을 Store에 dispatch 하는 기능구현
    • Reducer에서 바로 구현되지 않는 기타 Reducer 로직을 구현할 때는 extraReducer를 사용해 구현하게 되어있음.
    • 통신중일 때, 성공했을 때, 실패했을 때 이러한 케이스를 모두 상태로 관리하는 로직을 구현해야 함
    // src/redux/modules/todosSlice.js
    import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
    import axios from 'axios';
    
    const initialState = {
      todos: [], // 데이터
      isLoading: false, // 서버에서 todos를 가져오는 상태를 나타내는 값. 서버 통신이 시작되면 true였다가 통신이 끝나면 false로 변경되게 할거임.
      error: null, // 통신 실패 시 서버에서 어떤 에러메세지를 보내줄건데 그것을 담아놓는 값, 아직 에러가 없기에 null로 설정함.
    };
    
    //Thunk 함수
    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,
      reducers: {},
      extraReducers: {
        [__getTodos.pending]: (state) => {
          state.isLoading = true;
        },
        [__getTodos.fulfilled]: (state, action) => {
          state.isLoading = false;
          state.todos = action.payload;
        },
        [__getTodos.rejected]: (state, action) => {
          state.isLoading = false;
          state.error = action.payload;
        },
      },
    });
    
    export const {} = todosSlice.actions;
    export default todosSlice.reducer;
    • 대부분 서버와의 통신을 관리할 때에는 data, isLoading, error 로 관리하게 된다.
    • Axios는 Promise를 반환하기 때문에 Thunk함수 try 부분의 const data 는 Promise를 반환한다.
    • 반환된 Promise가 성공적으로 처리되었는지를 fullfilled 라고 한다.
    • 실패했는지는 rejected 되었다고 한다.
    • 이런것을 처리해주기 위해서 Async와 await을 추가해준다.
    • try와 catch문을 사용하여 통신에 성공, 실패했을 때 실행할 문을 작성한다.

    'React' 카테고리의 다른 글

    성능 최적화를 위한 React-hook  (0) 2022.12.21
    Redux 이용해서 TodoApp 만들기  (0) 2022.12.20
    styled component porps해서 쓰기  (0) 2022.12.20
    Redux(3) - react-router-dom, props children, Dynamic Route  (0) 2022.12.20
    Axios  (0) 2022.12.20
Designed by Tistory.