programing

ES6 제너레이터에서 redux-saga를 사용하는 것과 ES2017 비동기/대기에서 redux-thunk를 사용하는 것의 장단점

nicescript 2022. 9. 21. 22:26
반응형

ES6 제너레이터에서 redux-saga를 사용하는 것과 ES2017 비동기/대기에서 redux-thunk를 사용하는 것의 장단점

지금 레독스 타운에서는 레독스가/레독스가라는 최신 아이에 대한 이야기가 많이 나오고 있습니다.액션을 수신/디스패치하기 위해 제너레이터 기능을 사용합니다.

에 먼저 .redux-saga 어프로치 하고 있는 입니다.redux-thunk비동기/동기/동기/동기

컴포넌트는, 통상대로 디스패치 액션을 실시합니다.

import { login } from 'redux/auth';

class LoginForm extends Component {

  onClick(e) {
    e.preventDefault();
    const { user, pass } = this.refs;
    this.props.dispatch(login(user.value, pass.value));
  }

  render() {
    return (<div>
        <input type="text" ref="user" />
        <input type="password" ref="pass" />
        <button onClick={::this.onClick}>Sign In</button>
    </div>);
  } 
}

export default connect((state) => ({}))(LoginForm);

그러면 내 행동은 다음과 같습니다.

// auth.js

import request from 'axios';
import { loadUserData } from './user';

// define constants
// define initial state
// export default reducer

export const login = (user, pass) => async (dispatch) => {
    try {
        dispatch({ type: LOGIN_REQUEST });
        let { data } = await request.post('/login', { user, pass });
        await dispatch(loadUserData(data.uid));
        dispatch({ type: LOGIN_SUCCESS, data });
    } catch(error) {
        dispatch({ type: LOGIN_ERROR, error });
    }
}

// more actions...

// user.js

import request from 'axios';

// define constants
// define initial state
// export default reducer

export const loadUserData = (uid) => async (dispatch) => {
    try {
        dispatch({ type: USERDATA_REQUEST });
        let { data } = await request.get(`/users/${uid}`);
        dispatch({ type: USERDATA_SUCCESS, data });
    } catch(error) {
        dispatch({ type: USERDATA_ERROR, error });
    }
}

// more actions...

redux-saga에서 위의 예시와 동등한 것은 다음과 같습니다.

export function* loginSaga() {
  while(true) {
    const { user, pass } = yield take(LOGIN_REQUEST)
    try {
      let { data } = yield call(request.post, '/login', { user, pass });
      yield fork(loadUserData, data.uid);
      yield put({ type: LOGIN_SUCCESS, data });
    } catch(error) {
      yield put({ type: LOGIN_ERROR, error });
    }  
  }
}

export function* loadUserData(uid) {
  try {
    yield put({ type: USERDATA_REQUEST });
    let { data } = yield call(request.get, `/users/${uid}`);
    yield put({ type: USERDATA_SUCCESS, data });
  } catch(error) {
    yield put({ type: USERDATA_ERROR, error });
  }
}

할 것은 를 API 입니다.yield call(func, ...args)call게 ' 좋다' 같은 단순한 요.단순한 오브젝트를 생성할 뿐입니다.{type: 'CALL', func, args}을 위임하고그 redux-saga middleware는 redux-saga middleware, generator는 redux-saga middleware, generator.

주요 장점은 간단한 등식 검사를 사용하여 Redux 외부에서 생성기를 검정할 수 있다는 것입니다.

const iterator = loginSaga()

assert.deepEqual(iterator.next().value, take(LOGIN_REQUEST))

// resume the generator with some dummy action
const mockAction = {user: '...', pass: '...'}
assert.deepEqual(
  iterator.next(mockAction).value, 
  call(request.post, '/login', mockAction)
)

// simulate an error result
const mockError = 'invalid user/password'
assert.deepEqual(
  iterator.throw(mockError).value, 
  put({ type: LOGIN_ERROR, error: mockError })
)

호출 api 호출 를 api에 하는 것 입니다.next반복기의 메서드.데이터를 조롱하는 것은 기능을 조롱하는 것보다 훨씬 간단합니다.

해야 할 은 '콜입니다.yield take(ACTION) 액액예예예, 을을을을을을)에 대해 LOGIN_REQUEST). 즉, 액션은 지속적으로 thunks에 푸시되며, thunks는 이러한 액션의 처리를 정지할 타이밍을 제어할 수 없습니다.

레독스가에서는 발전기가 다음 액션을 당깁니다.즉, 어떤 액션을 들을 때와 들을 때를 제어할 수 있습니다.위의 예에서는 플로우 지시가 내부로 배치되어 있습니다.while(true)루프를 통해 들어오는 각 동작을 듣습니다. 이는 쿵크 푸쉬 동작과 어느 정도 유사합니다.

풀 어프로치를 사용하면 복잡한 제어 플로우를 구현할 수 있습니다.예를 들어 다음과 같은 요건을 추가한다고 가정합니다.

  • LOGOUT 사용자 작업 처리

  • 로그인에 는 토큰을 이 토큰은 어느 이 만료되어 「」에됩니다.이 토큰은 에 저장되어 있는 지연으로 만료됩니다.expires_in 각각의 허가서를 백그라운드에서 갱신해야 합니다.expires_in(밀리초 단위

  • API 콜의 결과(초기 로그인 또는 리프레시 중 하나)를 기다리는 경우 사용자가 중간에 로그아웃할 수 있음을 고려하십시오.

전체 흐름에 대한 완전한 테스트 범위를 제공하면서 어떻게 탠크를 사용하여 이를 구현합니까?Sagas는 다음과 같습니다.

function* authorize(credentials) {
  const token = yield call(api.authorize, credentials)
  yield put( login.success(token) )
  return token
}

function* authAndRefreshTokenOnExpiry(name, password) {
  let token = yield call(authorize, {name, password})
  while(true) {
    yield call(delay, token.expires_in)
    token = yield call(authorize, {token})
  }
}

function* watchAuth() {
  while(true) {
    try {
      const {name, password} = yield take(LOGIN_REQUEST)

      yield race([
        take(LOGOUT),
        call(authAndRefreshTokenOnExpiry, name, password)
      ])

      // user logged out, next while iteration will wait for the
      // next LOGIN_REQUEST action

    } catch(error) {
      yield put( login.error(error) )
    }
  }
}

예에서는 race.take(LOGOUT)경주에서 우승합니다(즉, 사용자가 로그아웃 버튼을 클릭).됩니다.authAndRefreshTokenOnExpiry이 일어난다면authAndRefreshTokenOnExpirycall(authorize, {token})콜도 취소됩니다.취소는 자동으로 아래로 전파됩니다.

흐름의 실행 가능한 데모를 찾을 수 있습니다.

도서관 저자의 꽤 철저한 답변에 덧붙여, 제작 시스템에 사가를 사용한 경험을 추가한다.

Pro(saga 사용):

  • 테스트 가능성sags를 테스트하는 것은 매우 간단합니다.call()은 순수 객체를 반환하기 때문입니다.트렁크를 테스트하려면 보통 테스트에 mockStore를 포함해야 합니다.

  • redux-timeout에는 태스크에 관한 유용한 도우미 기능이 많이 포함되어 있습니다.saga의 개념은 리액트 리덕스 아키텍처에서 누락된 부분으로 기능하는 앱의 백그라운드 워커/스레드를 만드는 것이라고 생각합니다(action Creators와 Reducer는 순수한 함수여야 합니다).다음 포인트로 이어지죠

  • 사가들은 모든 부작용을 처리할 수 있는 독립적인 장소를 제공한다.내 경험상 thunk 액션보다 보통 수정하고 관리하는 것이 더 쉽다.

단점:

  • 제너레이터 구문

  • 배워야 할 컨셉이 많네요.

  • API 안정성redux-saga는 아직 기능(예를 들어 Channels?)을 추가하고 있는 것 같고, 커뮤니티는 그다지 크지 않습니다.라이브러리가 언젠가 하위 호환성이 없는 업데이트를 할 경우 문제가 있습니다.

제 개인적인 경험에서 몇 가지 코멘트를 추가하고 싶습니다(sagas와 thunk를 모두 사용).

Saga는 테스트에 최적입니다.

  • 효과로 포장된 기능을 시뮬레이션할 필요가 없습니다.
  • 따라서 테스트는 깨끗하고 읽기 쉬우며 쓰기 쉽습니다.
  • saga를 사용할 때 액션 크리에이터는 대부분 일반 객체 리터럴을 반환합니다.그것은 또한 덩크의 약속과 달리 테스트하고 주장하기가 더 쉽다.

사가는 더 강력하다.하나의 Thunk의 액션 크리에이터로 할 수 있는 모든 것은 하나의 사가로도 할 수 있지만, 그 반대는 할 수 없습니다(적어도 쉽지는 않습니다).예를 들어 다음과 같습니다.

  • take)
  • 루틴을 cancel,takeLatest,race)
  • 액션을 수 (리슨할 수 있습니다.take,takeEvery , ... )

Sagas는 몇 가지 일반적인 애플리케이션 패턴을 일반화하는 기타 유용한 기능도 제공합니다.

  • channels외부 이벤트 소스(예: 웹소켓)를 청취합니다.
  • 모델Fork Model)fork,spawn)
  • 스로틀
  • ...

사가는 위대하고 강력한 도구입니다.하지만 힘에는 책임이 따른다.어플리케이션이 커지면 액션이 디스패치되기를 기다리는 사람이나 액션이 디스패치될 때 일어나는 모든 일을 파악함으로써 쉽게 잃어버릴 수 있습니다.반면에 덩크는 더 간단하고 쉽게 추론할 수 있다.하나 또는 다른 하나를 선택하는 것은 프로젝트의 유형 및 크기, 프로젝트가 처리하거나 팀 선호도를 개발해야 하는 부작용 유형 등 여러 측면에 따라 달라집니다.어떤 경우에도 애플리케이션을 단순하고 예측 가능한 상태로 유지하십시오.

2020년 7월 갱신:

지난 16개월 동안 React 커뮤니티에서 가장 눈에 띄는 변화는 React 훅입니다.

제가 관찰한 바에 따르면 기능 컴포넌트 및 후크와의 호환성을 높이기 위해 프로젝트(대형 프로젝트라도)는 다음을 사용하는 경향이 있습니다.

  1. hook + async thunk (hook은 모든 것을 매우 유연하게 만듭니다.따라서 실제로 비동기 thunk를 원하는 위치에 배치하여 일반 함수로 사용할 수 있습니다.예를 들어 tunk in action.ts를 쓰고 Dispatch()를 사용하여 thunk를 트리거할 수 있습니다.
  2. useRequest,
  3. GraphQL/ApollouseQuery useMutation
  4. 반응 페칭 개재성의
  5. 데이터 가져오기/API 콜 라이브러리, 도구, 설계 패턴 등의 기타 일반적인 선택지

에 비해, 「 」는 「 」입니다.redux-saga로선 위의 큰 많은 사가 으로써 프로젝트의 최신 1.의 최신 릴리스이기 때문입니다v1.1.1)을 클릭합니다.redux-saga2019년 9월 18일.오래 전입니다).

하지만 여전히,redux-saga는 레이싱 효과나 병렬 요구 등 몇 가지 고유한 기능을 제공합니다.이러한에는 다음과 같은 이 필요합니다.redux-saga여전히 좋은 선택입니다.


2019년 3월 오리지널 투고:

몇 가지 개인적인 경험:

  1. 코딩 스타일과 가독성을 위해 과거에 redux-saga를 사용했을 때의 가장 큰 장점 중 하나는 redux-thunk의 콜백헬을 피할 수 있다는 것입니다.즉, 많은 네스트/캐치를 사용할 필요가 없어집니다.그러나 현재 비동기/await thunk가 유행하고 있기 때문에 redux-thunk를 사용할 때 동기 스타일로 비동기 코드를 작성할 수도 있습니다.이것은 redux-thunk의 향상으로 간주될 수 있습니다.

  2. 특히 Typescript에서 redux-saga를 사용할 때는 훨씬 더 많은 보일러 플레이트 코드를 작성해야 할 수 있습니다.예를 들어 fetch 비동기 함수를 구현하는 경우 데이터와 오류 처리는 단일 FETH 액션으로 action.js의 하나의 thunk 단위로 직접 수행할 수 있습니다.그러나 redux-saga에서는 FETCH_START, FETCH_SUCCESS 및 FETCH_FAILURE 액션과 관련된 모든 유형 체크를 정의해야 할 수 있습니다.redux-saga의 기능 중 하나는 이러한 종류의 풍부한 "토큰" 메커니즘을 사용하여 쉽게 효과를 생성하고 redux 저장소를 지시하는 것이기 때문입니다.물론 이런 행동을 하지 않고도 사가를 쓸 수 있지만, 그것은 덩크와 비슷할 것이다.

  3. 파일 구조면에서는 redux-saga가 더 명확한 경우가 많은 것 같습니다.모든 sagas.ts에서 비동기 관련 코드를 쉽게 찾을 수 있지만, redux-thunk에서는 동작에서 이를 확인할 필요가 있습니다.

  4. 간단한 테스트는 레독스가의 또 다른 가중치 기능이 될 수 있습니다.이것은 정말 편리합니다.단, redux-saga "call" 테스트는 테스트에서 실제 API 호출을 수행하지 않으므로 API 호출 후에 사용할 수 있는 스텝의 샘플 결과를 지정해야 합니다.따라서 redux-saga로 쓰기 전에 사가와 그에 대응하는 sagas.spec.ts를 상세하게 계획하는 것이 좋습니다.

  5. 또한 Redx-saga는 병렬 태스크 실행, takeLatest/takeEvery, fork/spawn 등의 많은 고급 기능을 제공합니다.

결론적으로 저는 개인적으로 많은 경우나 중소형 앱에서는 비동기식/대기식 redux-thunk를 사용합니다.이렇게 하면 많은 보일러 플레이트 코드/액션/타이프를 절약할 수 있으며, 여러 sagas.t를 전환하여 특정 sagas 트리를 유지할 필요가 없습니다.그러나 매우 복잡한 비동기 로직과 동시성/병렬 패턴 등의 기능이 필요한 대규모 앱을 개발 중이거나 테스트 및 유지 보수(특히 테스트 기반 개발)에 대한 수요가 많은 경우 redux-sagas를 사용하면 생명을 구할 수 있습니다.

어쨌든 redex-saga는 redex 자체보다 어렵고 복잡하지 않으며, 핵심 개념과 API가 한정되어 있어 이른바 가파른 학습 곡선을 가지고 있지 않다.레덕스가를 배우는 데 적은 시간을 소비하는 것이 언젠가 자신에게 도움이 될지도 모른다.

Sagas는 제 경험에서 몇 가지 다른 대규모 React/Redux 프로젝트를 검토한 결과, 훨씬 테스트하기 쉽고 틀리기 어려운 코드 작성 방법을 개발자에게 제공했습니다.

.는 항상 에게 무슨 걱정 .yield첫 번째로 몇 가지 테스트를 작성하면 바로 알 수 있습니다.

나는 몇몇 프로젝트들을 보아왔다. 그들은 마치 MVC패튼의 컨트롤러처럼 취급되어왔고 이것은 곧 명백한 혼란이 되었다.

A가 필요로 하는 곳에 Sagas를 사용하는 것이 좋습니다.단일 이벤트와 관련된 B타입의 것을 트리거합니다.여러 액션을 절단할 수 있는 경우는 커스텀 미들웨어를 작성하고 FSA 액션의 메타 속성을 사용하여 트리거하는 것이 더 간단하다고 생각합니다.

Thunks vs Sagas

Redux-Thunk ★★★★★★★★★★★★★★★★★」Redux-Saga몇 가지 중요한 점에서 둘 다 Redux용 미들웨어 라이브러리입니다(Redux 미들웨어는 dispatch() 메서드를 통해 스토어에 들어오는 액션을 대행 수신하는 코드입니다).

액션은 말 그대로 모든 것이 될 수 있지만 베스트 프랙티스를 따를 경우 액션은 유형 필드 및 옵션 페이로드, 메타 및 오류 필드가 있는 일반 javascript 개체입니다.

const loginRequest = {
    type: 'LOGIN_REQUEST',
    payload: {
        name: 'admin',
        password: '123',
    }, };

리덕스-펑크

액션을 하는 것 에,Redux-Thunk하면, 「중간 기능」이라고 하는 한 기능을 할 수 .thunks.

Thunks(Redux의 경우)는 일반적으로 다음과 같은 구조를 가지고 있습니다.

export const thunkName =
   parameters =>
        (dispatch, getState) => {
            // Your application logic goes here
        };

아, 아, 아, 아.thunk는 (임의로) 일부 파라미터를 취득하고 다른 함수를 반환하는 함수입니다.는 ""를 합니다.dispatch function a. a. a.getState-둘 다 함수에 의해 됩니다.Redux-Thunk미들웨어

레독스가

Redux-Saga미들웨어를 사용하면 복잡한 애플리케이션 로직을 sagas라고 하는 순수한 함수로 표현할 수 있습니다.순수 함수는 예측 가능하고 반복 가능하기 때문에 검정 관점에서 바람직하며, 따라서 비교적 쉽게 검정할 수 있습니다.

사가는 발전기 기능이라고 불리는 특별한 기능을 통해 구현된다.은 것들은 these these these these these these these these these of of의 새로운 특징입니다.의 입니다.ES6 JavaScript기본적으로 항복문을 볼 때마다 제너레이터를 드나들며 실행이 이루어집니다.★★★★★★★★★★★★★★★★를 생각해 주세요.yield생성기가 일시 중지되어 반환된 값을 반환하는 것으로 간주됩니다. 후,는 「 」, 「 」, 「 」, 「 」에 할 수 .yield.

제너레이터 함수는 다음과 같이 정의됩니다.function 키워드 뒤에 아스타리스크가 붙어 있는 것에 주의해 주세요.

function* mySaga() {
    // ...
}

가 「」에 되면,Redux-Saga 그 ★★★★★★★★★★★★★★★★★★★★★★★.yield 수행은 이 "예"인 중지됩니다.'LOGIN_REQUEST'가게로 발송됩니다.그러면 실행이 계속됩니다.

자세한 것은, 이 문서를 참조해 주세요.

짧은 메모 하나.제너레이터는 취소 가능, 비동기/대기 가능 - 아닙니다.질문에서 예를 들면, 무엇을 선택해야 하는지 이해가 되지 않습니다.그러나 복잡한 흐름의 경우 발전기를 사용하는 것보다 더 나은 해결책은 없을 수 있습니다.

또 다른 아이디어는 레독스 트렁크를 가진 발전기를 사용하는 것입니다만, 저는 네모난 바퀴를 가진 자전거를 발명하려고 하는 것 같습니다.

물론 발전기는 테스트하기가 더 쉽습니다.

두 가지 장점을.redux-saga ★★★★★★★★★★★★★★★★★」redux-thunk: 에 대한 할 수 은 사가에 대한 것입니다.dispatching대응하는 액션:https://github.com/diegohaz/redux-saga-thunk

class MyComponent extends React.Component {
  componentWillMount() {
    // `doSomething` dispatches an action which is handled by some saga
    this.props.doSomething().then((detail) => {
      console.log('Yaay!', detail)
    }).catch((error) => {
      console.log('Oops!', error)
    })
  }
}

나는 최근에 이 프로젝트를 많이 활용했습니다.redux-saga 사가의 더 것을 .

TBH, 아직 찾고 있어요.이 글을 읽고 많은 사람들이 좋아하기 때문에, '프로'는 이해하기 어렵다.위의 답변은 다음과 같이 요약됩니다.

  1. 테스트 가능성(실제 API 콜 무시),
  2. 많은 도우미 기능,
  3. 서버측 코딩에 익숙한 개발자에게 친숙합니다.

다른 많은 주장들은 낙관적이고, 오해의 소지가 있거나, 단순히 잘못된 것처럼 보입니다!예를 들어, "엉뚱이는 X를 할 수 없다"는 부당한 주장을 많이 봐왔습니다.하지만 스컹크는 기능이다.함수가 X를 할 수 없는 경우 Javascript는 X를 할 수 없습니다.그래서 사가도 X를 할 수 없다.

나에게 있어 단점은 다음과 같다.

  • 의 교란generator커스텀 합니다.JS의 생성기는 사용자 지정 반복기를 반환합니다.할 수 있는 .비동기 콜을 처리하거나 취소할 수 있는 특별한 기능은 없습니다.모든 루프에는 브레이크아웃 조건이 있을 수 있으며, 모든 함수는 비동기 요구를 처리할 수 있으며, 모든 코드는 커스텀 반복기를 사용할 수 있습니다.이 다음과을 할 때:generators have control when to listen for some action ★★★★★★★★★★★★★★★★★」generators are cancellable, but async calls are not그런 다음 이러한 특성이 발전기 기능에 내재되어 있거나 심지어 고유하다는 것을 암시함으로써 혼란을 일으킨다.
  • 불분명한 사용 사례: AFAIK SAGA 패턴은 서비스 간 동시 트랜잭션 문제를 처리하기 위한 것입니다.스레드이기 이 어떻게 알 수.Promise를 알 수 BTW: 또한 왜 그 종류의 문제를 브라우저에서 처리해야 하는지 이해하기 어렵습니다.
  • 하여 레덕스를 바꿈dispatchSagas는 리듀서에 도달하지 않는 액션을 이벤트 처리와 같은 형태로 디스패치하기 때문에 Redux 툴에 기록되지 않습니다.다른 라이브러리도 이 작업을 수행하지만 브라우저에 이벤트 처리가 내장되어 있기 때문에 이 작업은 종종 불필요하게 복잡합니다.간접의 이점은 다시 이해하기 어려운데, 이는 사가를 직접 호출하는 것이 더 명확하기 때문입니다.

만약 이 투고가 내가 사가에게 좌절감을 느낀다면, 그것은 내가 사가에게 좌절감을 느꼈기 때문이다.그들은 해결할 문제를 찾는 훌륭한 해결책처럼 보인다.IMO.

보다 쉬운 방법은 redux-auto를 사용하는 것입니다.

문서상

redux-auto는 약속을 반환하는 "액션" 함수를 만드는 것만으로 이 비동기 문제를 해결했습니다."기본" 기능 동작 로직을 수반합니다.

  1. Thunk, Promise-Middleware, saga 등 다른 Redux 비동기 미들웨어가 필요 없음
  2. 쉽게 약속을 리덕스로 전달하여 관리할 수 있습니다.
  3. 외부 서비스 콜을 변환하는 장소와 함께 배치할 수 있습니다.
  4. 파일의 이름을 "init.js"로 지정하면 앱 시작 시 한 번 호출됩니다.이것은 시작할 때 서버에서 데이터를 로드하는 데 유용합니다.

각 액션을 특정 파일에 저장하여 서버 호출을 "보류", "완료" 및 "거부"를 위한 리듀서 함수와 함께 파일 내에 배치하는 것이 아이디어입니다.이를 통해 약속을 쉽게 처리할 수 있습니다.

또한 도우미 개체("async")를 상태 프로토타입에 자동으로 첨부하여 UI에서 요청된 전환을 추적할 수 있습니다.

언급URL : https://stackoverflow.com/questions/34930735/pros-cons-of-using-redux-saga-with-es6-generators-vs-redux-thunk-with-es2017-asy

반응형