-
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