Redux middleWare & useDispatch & useSelector

노영완·2023년 2월 10일
0

Redux

목록 보기
4/7
post-custom-banner

미들웨어란

리덕스를 사용하면서 비동기 작업을 다룰 때는 미들웨어가 있어야 더욱 손쉽게 상태를 관리 할 수 있습니다. 미들웨어는 액션이 디스패치 되면서 reducer로 전달하는 그 중간지로 생각하면 된다. 즉, dispatch 되어서 리듀서에서 이를 처리하기전에 사전에 지정된 작업들을 처리한다고 생각하면 된다.

미들웨어 만들어보기

삼단함수가 미들웨어다 삼단함수 모양이 미들웨어 형식이다.

const middleware = (store) => (next) => (action) => {
  // 여기 앞뒤로 action 전에 기능을 추가 할 수 있다.
  next(action);
  //여기 앞뒤로 action 후에 기능을 추가 할 수 있다.
};
// 구조 분해 할당화 하면
const middleware = ({dispatch, getState}) => (next) => (action) => {}

이제 실제 thunkmiddleware를 만들어 보면 다음과 같다.

const thunkMiddleware = (store) => (next) => (action) => {
  if (typeof action === "function") {
    // 비동기
    return action(store.dispatch, store.getState);
  }
  console.log("test", action);
  return next(action); // 동기
};

실제 redux-thunk와 차이점이 없는 코드이다 redux-thunk 레포를 들어가면 너무 간단한 코드여서 놀랄것이다.

applyMiddleware

store에 middleware를 적용할때에는 applyMiddleware 함수를 사용한다.

const store = createStore(
  reducer,
  initialState,
  applyMiddleware(thunkMiddleware)
);

또한 한개의 미들웨어가 아닌 여러개의 미들웨어를 연결할 수 도 있다.

const store = createStore(
  reducer,
  initialState,
  applyMiddleware(middleware,thunkMiddleware)
);

compose를 사용해 enhancer를 만들어보자

사실 enhancer를 만들어 store에 연결하는게 좋다. compose는 순차적으로 함수를 적용해 나가는 기능을 가지고 있다.

const enhancer = compose(applyMiddleware(middleware,thunkMiddleware))
const store = createStore(
  reducer,
  initialState,
  enhancer
);

실제 사용

// action에서 코드이다. setTimeout을 사용해 비동기처리를 추가
export const logIn = (data) => {
  // async action creator
  return (dispatch, getState) => {
    // async action
    dispatch(logInRequest(data));
    try {
      setTimeout(() => {
        dispatch(
          logInSuccess(data)
        );
      }, 2000);
    } catch (e) {
      dispatch(logInFailure(e));
    }
  };
};
// reduecer 코드이다.
import produce from "immer";
const initialState = {
  isLoggingIn: false,
  data: null,
};
export const userReducer = (prevState = initialState, action) => {
  return produce(prevState, (draft) => {
    switch (action.type) {
      case "LOGINREQUEST":
        draft.data = action.data;
        draft.isLoggingIn = true;
        break;
      case "LOGINSUCCESS":
        draft.data = action.data;
        draft.isLoggingIn = false;
        break;
      case "logInFailure":
        draft.data = action.data;
        draft.isLoggingIn = true;
      case "LOGOUT":
        draft.data = null;
      default:
        break;
    }
  });
};
const store = createStore(
  reducer,
  initialState,
  applyMiddleware(thunkMiddleware)
);


비동기처리 미들웨어를 만들고 store에 연결 후 action에 비동기처리 코드를 추가 시켜 미들웨어의 기능을 실행시켜주었고 reducer에는 각 action의 타입별로 store의 state를 저장해주게끔 만들어 dispatch(logIn(data))를 해줌으로써 위 사진과 같은 결과를 얻을 수 있다.
실제 따라해보면 LOGINSUCCESS type은 비동기 settimeout이 적용된 것을 볼 수 있을 것 이다.

useDispatch & useSelector

react-redux에서 제공하는 hook으로 일단 아래 명령어를 터미널에 입력.

npm i react-redux

두가지 hook은 connect함수, createAction을 사용하지 않고 useSelector를 통해 state에 접근할 수 있고 useDispatch로 생성한 action을 dispatch 할 수 있다.

기본세팅(연결)

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { store } from "./store";
import App from "./App";
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector("#root")
);

react-redux Provider를 가져오고 redux를 이용해서 만든 store를 연결해준다. 그리고 react 컴포넌트인 app을 연결해준다. 이렇게 되면 react-redux redux react 세개를 연결해 준 것이다.

사용해보기

import React, { useCallback, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
//react-redux에서 hook등을 가지고 온다.
import { logIn, logOut } from "./components/actions/User";
//dispatch 할 action들을 가지고 온다.
const App = () => {
  const user = useSelector((state) => state.user);
  //store에 저장하고 있는 state에 user로 접근해 
  //user 상수로 관리하겠다는 코드 이다.
  const posts = useSelector((state) => state.posts);
  const dispatch = useDispatch();
  //생성한 action을 useDispatch를 통해 발생시킬 수 있다.
  const onClick = () => {
    dispatch(logIn({ id: 1, nickname: "안녕" }));
  };
  const onLogout = () => {
    dispatch(logOut());
  };
  return (
    <div>
      {user.isLoggingIn ? (
        <div>로그인 중</div>
      ) : user.data ? (
        <div>{user.data.nickname}</div>
      ) : (
        "로그인 해주세요."
      )}
      {!user.data ? (
        <button onClick={onClick}>로그인</button>
      ) : (
        <button onClick={onLogout}>로그아웃</button>
      )}
    </div>
// useSelector로 store에 state를 가져와 Logging에 대한 핸들링
// data에 유무에 따라 nickname이 보이고 버튼이 달라지게끔 핸들링
  );
};
export default App;
post-custom-banner

0개의 댓글