How to wait for complex redux-saga action flow to

2019-07-23 21:05发布

I'm having issues with waiting "one action flow" to finish from start until the end. The app works as usual in client, but while doing stuff server-side it doesn't finish until the end.

To clarify, I'm using both "next-redux-wrapper" and "next-redux-saga" properly, as I can achieve the delayed rendering when I use simpler flow with sagas.

I think the mistake is mostly how I understand (or not) saga effects altogether.

Some of the code is omitted for brevity.

api.saga.js

const makeRequest = ({ body, url, method }) =>
  axios({ method, url: url, data: body });


export function* requestWatcher(action) {
  const { feature } = action.meta;

  try {
    const { data: response } = yield call(makeRequest, action.meta);
    yield put(apiSuccess({ response, feature }));
  } catch (error) {
    yield put(apiError({ error: error, feature }));
  }
}

export default function* apiSaga() {
  yield takeEvery(action => action.type.includes(API_REQUEST), requestWatcher);
}

smallBusiness.saga.js

function* watchApiSuccess(action) {
  yield put(
    setPositions({ positions: action.payload.positions })
  );
  yield put(setLoader({ isLoading: false, feature: SMALL_BUSINESS }));
}

function* watchApiError() {
  yield put(setLoader({ isLoading: false, feature: SMALL_BUSINESS }));
}

function* watchFetchPositions() {
  yield put(
    apiRequest({
      body: null,
      method: 'GET',
      url: SMALL_BUSINESS_URL,
      feature: SMALL_BUSINESS
    })
  );
  yield put(setLoader({ isLoading: true, feature: SMALL_BUSINESS }));
}

export default function* smallBusinessSaga() {
  yield all([
    takeLatest(`${SMALL_BUSINESS} ${API_SUCCESS}`, watchApiSuccess),
    takeLatest(`${SMALL_BUSINESS} ${API_ERROR}`, watchApiError),
    takeLatest(FETCH_POSITIONS, watchFetchPositions)
  ]);
}

rootSaga.js

export default function* rootSaga() {
  yield all([call(smallBusinessSaga), call(apiSaga)]);
}

configureStore.js

store.sagaTask = sagaMiddleware.run(rootSaga);

Client-side function "watchApiSuccess" is called, but server-side it's not. Every generator function is called server-side except for the one already mentioned.

When I simplify the flow with something like the code example below, app will pre-render server-side.

function* watchPrefetchPositions() {
  const meta = {
    body: null,
    method: 'GET',
    url: SMALL_BUSINESS_URL,
    feature: SMALL_BUSINESS
  };

  const { data: response } = yield call(makeRequest, meta);
  yield put(setPositions({ positions: response.positions }));
}

export default function* smallBusinessSaga() {
  yield all([
    ...
    takeLatest(PRE_FETCH_POSITIONS, watchPrefetchPositions)
  ]);
}

The main issue I have with being unable to have complex flows is that sagas won't be able to do things like normalize and more.

1条回答
何必那么认真
2楼-- · 2019-07-23 21:48

You can use flags in order to control when and if your components should render. This is a common solution for rendering a fallback UI (e.g: a spinner or a text) in order to wait until an async process (saga, thunk, API service etc) is finished and the component has all it needs to render itself.

I have attached a CodeSandBox example here so you can see how it works in a glimpse. https://codesandbox.io/embed/2586j3k1p

You can see that loading appears for a moment until the data is fetched. Note that CodeSandBox is caching this API call so you barely notice the loading text after the first click).

React road-map includes a release for a feature dealing with Async Rendering called "Suspense", which imitates the same behavior for this specific type of flow issues as you described. Until React Async Rendering will be released I recommend using flags.

If you would like more details, I recommend watching Dan Abramov: Beyond React 16 | JSConf Iceland 2018

Good Luck

查看更多
登录 后发表回答