Redux 미들웨어 2

Lee Soyeon·2022년 3월 25일
0

redux

목록 보기
4/4

redux-saga

  • 제너레이터 객체를 만들어내는 제너레이터 생성함수를 이용
  • 리덕스 사가 미들웨어를 설정하고, 내가만든 사가 함수를 등록한 후,
    사가 미들웨어를 실행. 그 후, 등록된 사가 함수를 실행할 액션을 디스패치하면 된다.

npm i redux-saga
또는
npm i next-redux-saga

//src > redux > store.js

import history from "./history";
import { routerMiddleware } from 'connected-react-router';
import createSagaMiddleware from "redux-saga";

const sagaMiddleware = createSagaMiddleware();

const store = createStore(
	reducer,
	composeWithDevTools(
		applyMiddleware(
          thunk.withExtraArgument({ history }), 			  promise,
          routerMiddleware(history),
          sagaMiddleware
        )
    )
);

// users.js
import { put } from "redux-saga/effects";

const GET_USERS_SAGA_START = "GET_USERS_SAGA_START";

function* getUsersSaga(action) {
  try {
    yield put(getUsersStart())
    yield delay(2000);
    const res = yield call(axios.get, "")
    yield put(getUsersSuccess(res.data));
    yield put(push("/")); // push는 connected-react-router의 모듈
  } catch(error) {
      yield put(getUsersFail(error));
    }
  }

export function getUsersSagaStart() {
  return {
    type: GET_USERS_SAGA_START
  };
}

// 만든 saga함수를 saga-middleware에 등록시켜야 하는데,
// users라는 리듀서 안에서 만들어진 사가함수를 한 곳(rootSaga)에 모아서 미들웨어에 전달
export function* usersSaga() {
  yield takeEvery(GET_USERS_SAGA_START, getUsersSaga)
}


// src > redux > modules > rootSaga.js
  
export default function* rootSaga() {
  yield all([usersSaga()]);
}


// UserListContainer.jsx
import { getUsersSagaStart } from "../redux/modules/users";

export default function UserListContainer() {
  const users = useSelector((state) => stte.users.data);
  const dispatch = useDispatch();
  
  const getUsers = useCallback(() => {
    dispatch(getUsersSagaStart());
  }, [dispatch]);
  
  return <UserList users={users} getUsers={getUsers} />;


// store.js

const store = createStore(
  ... // 생략(궁금하면 위쪽 코드 참고)
);
  // saga함수를 saga-middleware에 등록
  // store가 생긴 후에 설정해줘야 해서 아래쪽에 코드 추가
sagaMiddleware.run(rootSaga);

redux-saga의 주요 effect

  1. delay: 설정된 시간 이후에 resolve하는 Promise객체를 리턴
    ex) delay(1000)

  2. put: 특정 액션을 dispatch하도록 한다
    ex) put({type: 'INCREMENT'})

  3. takeEvery: 들어오는 모든 액션에 대해 특정 작업을 처리해준다 (비동기적으로 동작, 동기적으로 동작시키려면
    while(true) {yield take()} 를 사용하면 됨(yield는 일회용이라서 계속 사용하기 위해서 while(true)라는 조건문 안에 넣어주기))
    ex) takeEvery(INCREMENT_ASYNC(액션타입), increaseSaga(사가함수))

  4. takeLatest: 기존에 진행중이던 작업이 있다면 취소처리하고, 가장 마지막으로 실행된 작업만 수행
    ex) takeLatest(DECREASE_ASYNC, decreaseSaga)
    프론트 쪽에서만 요청을 취소해주고, 백엔드쪽에는 요청이 간 것을 취소 못함 > 백엔드에는 이미 데이터들이 있기 때문에 새로고침을 하면, 취소한 요청들까지 화면에 보일 수 있음 > 백엔드의 데이터도 삭제해줘야 함 (ex)로그인버튼 두번 연속클릭 같은 경우
    > throttle(액션타입, 사가함수, 시간(초단위)): 해당 시간동안은 해당 액션타입은 딱 한번만 실행할 수 있게 됨 (디바운싱과 비교)

  5. call: 함수의 첫 번째 파라미터는 함수, 나머지 파라미터는 해당 함수에 넣을 인수이다.
    ex) call(delay, 1000) === delay(1000)

  6. all: all함수를 사용해서 제너레이터 함수를 배열의 형태로 인자로 넣어주면, 제너레이터 함수들이 병행적으로 동시에 실행되고, 전부 resolve될 때까지 기다린다 (Promise.all과 비슷)
    ex) yield all([testSaga1(), testSaga2()]) > testSaga1과 testSaga2가 동시에 실행되고, 모두 resolve될 때까지 기다린다

  7. select: reducer에 있는 상태를 redux-saga에서 가져와서 사용할 수 있게 해준다

  8. fork: call과 다르게 비동기적으로 작동한다(요청 안기다리고 바로 다음 것 실행)

  9. take: 액션을 기다린다

  10. takeLeading: 맨 첫번째 것만 실행(takeLatest와 반대)


redux-actions

npm i redux-actions
: ducks pattern을 쉽게 구현할 수 있도록 도와주는 라이브러리

  1. createAction: 액션을 만들어주는 함수(?)
// createAction("액션타입")("payload"): 액션타입 1개일 때 사용
import {createAction} from 'redux-actions';
createAction("액션타입")("payload")

// createActions("액션타입")("payload"): 액션타입 여러개일 때 사용
import {createActions} from 'redux-actions';
export const { 액션1생성함수명, 액션2생성함수명 } = createActions("액션타입1","액션타입2", { prefix: "파일명/리듀서명" });
//prefix부분: ducks pattern에서 타입만들때 앞에 붙히는 것 만들어주는 부분
  1. handleActions: 리듀서 만들어주는 함수
const reducer = handleActions(
  {
    SHOW_ALL: (state, action) => "ALL",
    SHOW_COMPLETE: () => "COMPLETE"
  },
	initialState,
  { prefix: "파일명/리듀서명" }
);
profile
프론트엔드 개발자가 되기 위해 공부하고 있습니다.

0개의 댓글