Redux MiddleWare -2 비동기 작업을 처리하는 미들웨어 (REACT_THUNK)

CCY·2020년 6월 22일
0

Redux

목록 보기
8/8

대표적인 비동기 미들웨어

  • redux-thunk:
    비동기 작업을 처리할 때 가장 많이 사용하는 미들웨어이며, 객체가 아닌 함수 형태의 액션을 디스패치 할 수 있게 해줌.
  • redux-saga:
    redux-thunk 다음으로 가장 많이 사용되는 비동기 작업 관련 미들웨어 라이브러리 입니다, 특정 액션이 디스패치 되었을때 정해진 로직에 따라 다른 액션을 디스패치 시키는 규칙을 작성하여 비동기 작업을 처리하게 할 수있게 해줌.

Redux-Thunk 예시

THUNK란?

  • 특정 작업을 나중에 할 수 있도록 미루기 위해 함수 형태로 감싼 것
//일반 적으로 
const addOne =x => x+1
console.log(addOne(1)) 하면 
정답은 2가 나온다.

//thunk 적용
const addOne =x => x+1
function addOneThunk(x){
  const thunk =()=>addOne(x)
  return thunk
}

const fn = addOneThunk(1);
setTimeout(() => {
  const value = fn();
  console.log(value);
}, 1000);

이렇게 하면 1 이 출력되고 1초 이따가 2가 출력됨.

redux-thunk 적용해보기

syntax:


const sampleThunk = () => (dispatch,getState) =>{
  //현 상태를 참조 할 수 있고,
  // 새 액션을 디스패치 할 수도 있음.
}

실제 적용 1

설치:

//설치
yarn add redux-thunk / npm install redux-thunk

스토어에 연결

import ReduxThunk from "redux-thunk"
const store = createStore(rootReducer, applyMiddleware(ReduxThunk))

비동기 디스패치 함수를 추가

//1초 뒤에 increase 혹은 decrease 함수를 디스패치함
export const increaseAsync = () => (dispatch) => {
  setTimeout(() => {
    dispatch(increase())
  }, 1000)
}
export const decreaseAsync = () => (dispatch) => {
  setTimeout(() => {
    dispatch(decrease())
  }, 1000)
}

Actions 호출

import React from "react"
import { connect } from "react-redux"
import { increaseAsync, decreaseAsync } from "../modules/counter"
import Counter from "../components/Counter"
const CounterContainer = ({ number, increaseAsync, decreaseAsync }) => {
  return <Counter number={number} onIncrease={increaseAsync} onDecrease={decreaseAsync} />
}
export default connect(
  (state) => ({
    number: state.counter,
  }),
  { increaseAsync, decreaseAsync }
)(CounterContainer)


1 초 간격으로 +1이 되고 -1 이됨

실제 적용 2 (웹요청 비동기 처리하기)

  1. api 파일을 만듬
import axios from "axios"

export const getPost = (id) => axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)

export const getUsers = (id) => axios.get(`https://jsonplaceholder.typicode.com/users`)
  1. 액션 및 리듀셔 만듬
//리듀서 만들기

import { handleActions } from "redux-actions"
import * as api from "../lib/api"
import createRequestThunk from "../lib/createRequestThunk"

//액션타입을 선언
// 한 요청당 세 개를 만들어야 함

const GET_POST = "sample/GET_POST"
const GET_POST_SUCCESS = "sample/GET_POST_FAILURE"
const GET_POST_FAILURE = "sample/GET_POST_FAILURE"

const GET_USERS = "sample/GET_USERS"
const GET_USERS_SUCCESS = "sample/GET_USERS_SUCCESS"
const GET_USERS_FAILURE = "sampe/GET_USERS_FAILURE"

//Thunk 함수 생성
//Thunk 함수 내부에서 시작할떄, 성공했을때, 실패했을때 액션들을 디스패치

export const getPost = createRequestThunk(GET_POST, api.getPost)
export const getUsers = createRequestThunk(GET_USERS, api.getUsers)


초기 상태를 선언합니다
요청의 로딩 중 상태는 loading 이라는 객체를 관리함

const initialState = {
  loading: {
    GET_POST: false,
    GET_USERS: false,
  },
  post: null,
  users: null,
}

const sample = handleActions(
  {
    [GET_POST]: (state) => ({
      ...state,
      loading: {
        ...state.loading,
        GET_POST: true, //요청 시작
      },
    }),
    [GET_POST_SUCCESS]: (state, action) => ({
      ...state,
      post: action.payload,
    }),
    [GET_POST_FAILURE]: (state, action) => ({
      ...state,
      loading: {
        ...state.loading,
        GET_POST: false, //요청 완료
      },
    }),
    [GET_USERS]: (state) => ({
      ...state,
      loading: {
        ...state.loading,
        GET_USERS: true, //요청 시작
      },
    }),
    [GET_USERS_SUCCESS]: (state, action) => ({
      ...state,
      users: action.payload,
    }),
    [GET_USERS_FAILURE]: (state, action) => ({
      ...state,
      loading: {
        ...state.loading,
        GET_USERS: false,
      },
    }),
  },
  initialState
)
export default sample
  1. store 와 컴포넌트 connect 함
import React from "react"
import { connect } from "react-redux"
import Sample from "../components/Sample"
import { getPost, getUsers } from "../modules/sample"

const { useEffect } = React
const SampleContainer = ({ getPost, getUsers, post, users, loadingPost, loadingUsers }) => {
  useEffect(() => {
    const fn = async () => {
      try {
        await getPost(1)
        await getUsers(1)
      } catch (e) {
        console.log(e) //에러 조회
      }
    }
    fn()
  }, [getPost, getUsers])

  return (
    <Sample post={post} users={users} loadingPost={loadingPost} loadingUsers={loadingUsers} />
  )
}

export default connect(
  ({ sample, loading }) => ({
    post: sample.post,
    users: sample.users,
    loadingPost: loading["sample/GET_POST"],
    loadingUsers: loading["sample/GET_USERS"],
  }),
  { getPost, getUsers }
)(SampleContainer)

어떻게 되는지 새로고침을 하긴 했는데 비동기처리 되어 페이지가 로딩후 출력되는 기능이다.

profile
✍️ 기록을 습관화 하자 ✍️ 나는 할 수 있다, 나는 개발자가 될거다 💪🙌😎

1개의 댓글

comment-user-thumbnail
2021년 3월 19일

안녕하세요^^ 저 궁금한게 저런 이미지 뭘로 찍으신거에요? GIF처럼?

답글 달기