React) Redux로 비동기 제어하기

미누도 개발한다·2021년 6월 5일
0

리액트

목록 보기
3/11

비동기 코드는 리덕스 함수에서 바로 처리하면 안되고, redux-thunk를 통해 action creator 함수에서 처리해주어야한다.
까먹지 않게 구현부분만 기록해 놓자.

App.js

import {Fragment,useEffect} from 'react';
import {useDispatch,useSelector} from 'react-redux';

import Cart from './components/Cart/Cart';
import Layout from './components/Layout/Layout';
import Products from './components/Shop/Products';
import Notification from './components/UI/Notification';
import {uiActions} from './store/ui-slice';
import { sendCartData } from './store/cart-slice';

let isInitial = true; // avoid-first-rendering 

function App() {
  const dispatch = useDispatch();
  const showCart = useSelector(state => state.ui.cartIsVisible);
  const cart = useSelector((state)=>state.cart);
  const notification = useSelector((state)=>state.ui.notification)

  useEffect(()=>{
    if(isInitial){
      isInitial = false;
      return;
    }
    dispatch(sendCartData(cart)); // dispatch(async()) 가 됨 -> 리덕스가 async함수를 실행해줌
    console.log('main으로 빠짐,useEffect종료');
  },[cart]);
  return (
    <Fragment>
      {notification && <Notification status={notification.status} title={notification.title} message={notification.message}/>}
      <Layout>
        {showCart && <Cart />}
        <Products />
      </Layout>
    </Fragment>
  );
}

export default App;

다룰 부분은, cart store가 변했을때, App이 리렌더링 되면서, dispatch(sendCartData(cart)) 가 호출되는 부분이다.

목표 동작: cart store 상태를, firebase에 저장한다.

비동기는 reducer에서 처리하면안된다 . 따라서 action creator 함수에서 처리되도록 한다.

이 개념을 위해서 redux-thunk에 대한 이해가 필요하다.

dispatch의 매개변수부분을 보면, async함수를 받고있다. 이를 thunk가 처리해준다.

즉, dispatch 에서 매개함수를 받게되면 리덕스가 매개함수를 실행 해주는데, 이때 매개함수의 매개변수로 dispatch를 전달해준다.

코멘트들은 실행흐름을 알기위해서 추적하느라 달았다(nested 된 async함수가 들어가서 좀 헷갈렸기 때문에)

cart-actions.js

import { uiActions } from './ui-slice';
import { cartActions } from './cart-slice';

export const sendCartData = (cart) => {
    return async(dispatch) => {
        console.log('첫번쨰 async함수');
        dispatch(uiActions.showNotification({
            status:'pending',
            title:'sending....',
            message:'sending cart data'
        }));
        
        const sendRequest = async() => {
            console.log('두번째 async함수');
            const response = await fetch(
                'https://react-httprequest-default-rtdb.firebaseio.com/cart.json',
                {method:'PUT',body:JSON.stringify({items: cart.items, totalQuantity:cart.totalQuantity})}
            );
            if(!response.ok){
                throw new Error('Seding cart data failed.');
            }
            
        }
        try{
            await sendRequest(); // App의 useEffect 스코프로 빠지는 시점, // sendReqeust함수전체의 값을 기다리고, 에러발생시 여기 promise자리로 error가 전달됨
            console.log('두번째 async함수 종료');
            dispatch(
                uiActions.showNotification({
                    status:'success',
                    title:'Success!',
                    message:'Sent cart data successfully!',
                })
            );
        }catch(error){
            // 여기서 dispatch 에러처리를 한다. 에러 디스패치 처리하고 가장 바깥 async함수가 종료된다. 결과값은 함수를 호출한 main의 promise자리의 result값으로 들어감
            dispatch(
                uiActions.showNotification({
                    status:'error',
                    title:'Error!',
                    message:'Sending cart data failed!',
                })
            )
            
        }
        console.log('첫번째 async함수 정상종료');
        
    }
}

App.js에서, dispatch(sendCartData(cart)) 부분을 보면,

sendCartData(cart) 는 async function 을 리턴하는데, 해당 함수스코프에서 cart를 참조할 수 있다.


ui-slice.js
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
    cartIsVisible:false,notification:null
};

const uiSlice = createSlice({
    name:'ui',
    initialState:initialState,
    reducers: {
        toggle(state) {
            state.cartIsVisible = !state.cartIsVisible;
        },
        showNotification(state,action){
            state.notification={
                status:action.payload.status,
                title:action.payload.title,
                message:action.payload.message
            };
        }

    }
});
export const uiActions = uiSlice.actions;
export default uiSlice;
profile
빨리 가는 유일한 방법은 제대로 가는 것이다

0개의 댓글