(React.js) React_redux-saga

redux-saga를 사용한 비동기 작업 처리

비동기 작업을 처리하는 라이브러리

리덕스에서 조치 후, 비동기 처리통해 상태 값 변경당신이 원하는 경우 사용

  • redux-thunk
  • redux-observable (RxJS 패키지 기반)
  • redux 사가

: 발전기 기반, 사용하기 쉽고 테스트 코드를 작성하기 쉽습니다.

발전기

발전기는 기능*로 작성

function* f1() {
  console.log('f1-1');
  yield 10;
  console.log('f1-2');
  yield 20;
  console.log('f1-3');
  return 'finished';
}
const gen = f1();
console.log(gen.next());
/*
  실행구간
  console.log('f1-1');
  yield 10;

  반환객체
  { value: 10, done: false }
*/

console.log(gen.next());
/*
  실행구간
  console.log('f1-2');
  yield 20;

  반환객체
  { value: 20, done: false }
*/

console.log(gen.next());
/*
  실행구간
  console.log('f1-3');
  yield 30;

  반환객체
  { value: 'finished', done: true }
*/

다음 기능 (제너레이터 객체 내부의 함수)

발전기의 수율 간격기반으로호출로 나누어

: 권리 가치 산출 / 완료: 모든 생성기 기능이 완료되었는지 여부

반복자 대 반복 가능

반복자 반복 가능
다음 기능 사용 가능 Symbol.iteraor() 재산 가치로 가지고
다음 함수: value/done 속성 값의 개체를 반환합니다. Symbol.iteraor()가 호출되면 반복자를 반환합니다.
작업이 끝나면 done ==true

제너레이터는 이터레이터이자 이터러블입니다.안돼.

이 때문에 for of 문과 스프레드 연산자를 사용할 수 있습니다.

function* f1() {
  yield 10;
  yield 20;
  yield 30;
}

for (const v of f1()) {
  console.log(v);
}

const arr = (...f1());
console.log(arr); // ( 10, 20, 30 )

발전기 예

function* user() {
  const userMessageList = ('Hi, I am keeper!', 'Nice to meet you!');
  for (const message of userMessageList) {
    console.log('User: ', yield message);
  }
}

function kepper() {
  const gen = user();

  const robotMessageList = ('', 'Hi, I am user!');
  for (const message of robotMessageList) {
    console.log('Keeper: ', gen.next(message).value);
  }
}

제너레이터 next에 값을 인자로 전달하면 전달된 인자는 수율의 반환 값

  1. gen.next(”)=> 로그: 키퍼: 안녕하세요, 키퍼입니다!
  2. gen.next(‘Hi, I am user!’)=> 로그: User: 안녕하세요, 저는 User입니다!
  3. 반복하다

Redux 사가 함수: user()

Redux Saga 미들웨어: keepper()

Redux Saga 효과 기능

놓다 : 동작 발생

부르다 : 서버 API 호출

테이크리딩 (<-> 최신)

두 번째 인수 함수가 아직 진행 중입니다.만약에 사이의 작업 무시하다 먼저 들어온 조치가 우선합니다.할 수 있습니다.

import { all, call, put, takeLeading } from 'redux-saga/effects';

export function* fetchData(action) {
  yield put(actions.setLoading(true));
  yield put(actions.addLike(action.timeline.id, 1));
  yield call(callApiLike);
  yield put(actions.setLoading(false));
}

export default function* () {
  yield all((
    takeLeading(type.REQUEST_LIKE, fetchData)
    // takeLeading(type.REQUEST_LIKE1, fetchData1)
    // takeLeading(type.REQUEST_LIKE2, fetchData2)
    // takeLeading(type.REQUEST_LIKE3, fetchData3)
  ));
}

함수 반환 객체 가져오기

var takeReturn = take(types.REQUEST_LIKE);
var takeReturnLog = {
  TAKE: {
    pattern: 'timeline/REQUEST_LIKE'
  }
}

put 함수 반환 객체

var putReturn = put(actions.setLoading(false));
var putReturnLog = {
  PUT: {
    channel: null,
    action: {
      type: 'timeline/SET_LOADING',
      isLoading: false
    }
  }
}

호출 함수 반환 객체

var callReturn = call(callApiLike);
var callReturnLog = {
  CALL: {
    context: null,
    fn: callApiLike,
    args: (),
  }
}

반환된 객체는 yield를 호출할 때 반환됩니다. saga 미들웨어로 전달

Saga 미들웨어는 부작용이 완료된 후 결과 와 함께 실행 흐름을 생성기(fetchData)로 다시 전달합니다.

redux saga 미들웨어 추가

import { createStore, combineReducers, compose, applyMiddleware } from 'redux';
import { createSagaMiddleware } from 'redux-saga';
import { all } from 'redux-saga/effects';

const reducer = combineReducers({
  timeline: timelineReducer,
  friend: friendReducer,
})

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const sagaMiddleware = createSagaMiddleware();

const store = createStore(
  reducer,
  composeEnhancers(
    applyMiddleware(sagaMiddleware)
  )
)

function* rootSaga() {
  yield all((
    timelineSaga(),
    friendSaga()
  ));
}
sagaMiddleware.run(rootSage);

export default store;
  • 기존 redux 애플리케이션 코드에서 createSaga미들웨어() 사용하여 생성
  • createStore에서, 미들웨어 등록 composeEnhancers 기능을 입력, 적용미들웨어 함수에 미들웨어 객체 넣기주다.(다수의 applyMiddleware 입력 가능)
  • all 함수를 통해 모든 saga를 추가한 후 saga 미들웨어를 실행합니다.

여러 작업 처리

LOGIN 및 LOGOUT 작업 처리

function* loginFlow() {
  while(true) {
    // (로그인)
    // 로그인 액션을 기다림
    // 로그인 API 호출
    // 로그인 정보를 저장
    const { id, password } = yield take(types.LOGIN);
    const userInfo = yield call(callApiLogin, id, password);
    yield put(types.SET_USER_INFO, userInfo);
    
    // (로그아웃)
    // 로그아웃 액션을 기다림
    // 로그아웃 API 호출
    // 로그인 정보를 제거
    yield take(types.LOGOUT);
    yield call(callApiLogout, id);
    yield put(types.SET_USER_INFO, null);
  }
}

Redux 사가 예외 처리

try/catch 문을 사용하여 예외 처리

import { all, call, put, takeLeading } from 'redux-saga/effects';

export function* fetchData(action) {
  yield put(actions.setLoading(true));
  yield put(actions.addLike(action.timeline.id, 1));
  
  // 에러처리
  try {
    yield call(callApiLike);
  } catch (error) {
    // 에러객체 저장
    // Like 원래상태로 되돌림
    yield put(actions.setValue('error', error));
    yield put(actions.addLike(action.timeline.id, 1));
  }
  
  yield put(actions.setLoading(false));
}

export default function* () {
  yield all((
    takeLeading(type.REQUEST_LIKE, fetchData)
  ));
}

디바운스

: 동일한 함수가 연속으로 여러 번 호출될 때 첫 번째 또는 마지막 호출만 실행하는 기능

Reduxa saga 디바운스 기능

일정 시간 내 생성기 기능 여러 번 호출해도 한 번만 실행

REQUEST_LIKE 액션이 발생하면 바로 함수를 호출하지 않고 1초간 기다린다. 더 이상 작업이 발생하지 않으면 fetchData 함수를 실행합니다.

import { all, call, put, takeLeading } from 'redux-saga/effects';

export function* fetchData(action) {
  yield put(actions.setLoading(true));
  yield put(actions.addLike(action.timeline.id, 1));
  try {
    yield call(callApiLike);
  } catch (error) {
    yield put(actions.setValue('error', error));
    yield put(actions.addLike(action.timeline.id, 1));
  }
  yield put(actions.setLoading(false));
}

export default function* () {
  yield all((
    debounce(1000, type.REQUEST_LIKE, fetchData)
  ));
}

원천 : https://keeper.44?category=940178

(React.js) React_redux-saga

redux-saga를 이용한 비동기 액션 처리 비동기 액션을 처리하는 라이브러리 Redux redux-thunk redux-observable(RxJS 패키지)에서 액션을 처리한 후 비동기 처리를 통해 상태 값을 변경하고자 할 때 사용

keeper.tistory.com