React redux Sagas, wait for a Saga to set state

Viewed 414

I have some component with a code like this:

const startLogin = (code) => {
  dispatch(login({ code }));
  const publicKeyFromLocalST = window.localStorage.getItem('push_public_key');
  setPublicKey(publicKeyFromLocalST);
  // etc

When I dispatch the saga login it will store some data in localStorage. I need to execute the 3rd line (setPublicKey) after that data be actually indeed in localStorage.

How can "await" for dispatch(login({ code })); to be completed before setPublicKey?

1 Answers

Two options:

  1. Execute the setPublicKey function inside the worker saga, you can control the workflow in the worker saga easily with yield.
function* login(action) {
  const response = yield call(apiCall);
  if (response.error) {
    yield put({ type: actionType.LOGIN_FAIL });
  } else {
    yield put({ type: actionType.LOGIN_SUCCESS, data: response.data });
    const publicKeyFromLocalST = window.localStorage.getItem('push_public_key');
    setPublicKey(publicKeyFromLocalST);
  }
}
  1. Promisify the dispatch(login({code})), you should create a helper function like this:
const loginAsyncCreator = (dispatch) => (payload) => {
  return new Promise((resolve, reject) => dispatch(loginCreator(payload, { resolve, reject })));
};

You need to pass the resolve/reject to worker saga via action.meta, then you can decide when to resolve or reject the promise. Then, you can use async/await in your event handler. See below example:

import { call, put, takeLatest } from 'redux-saga/effects';
import { createStoreWithSaga } from '../../utils';
const actionType = {
  LOGIN: 'LOGIN',
  LOGIN_FAIL: 'LOGIN_FAIL',
  LOGIN_SUCCESS: 'LOGIN_SUCCESS',
};
function apiCall() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ error: null, data: 'login success' });
    }, 2000);
  });
}

function* login(action) {
  console.log('action: ', action);
  const {
    meta: { resolve, reject },
  } = action;
  const response = yield call(apiCall);
  console.log('response: ', response);
  if (response.error) {
    yield put({ type: actionType.LOGIN_FAIL });
    yield call(reject, response.error);
  } else {
    yield put({ type: actionType.LOGIN_SUCCESS, data: response.data });
    yield call(resolve, response.data);
  }
}

function* watchLogin() {
  yield takeLatest(actionType.LOGIN, login);
}

const store = createStoreWithSaga(watchLogin);

function loginCreator(payload, meta) {
  return {
    type: actionType.LOGIN,
    payload,
    meta,
  };
}

const loginAsyncCreator = (dispatch) => (payload) => {
  return new Promise((resolve, reject) => dispatch(loginCreator(payload, { resolve, reject })));
};

const loginAsync = loginAsyncCreator(store.dispatch);

async function startLogin() {
  await loginAsync({ code: '1' });
  console.log('setPublicKey');
}

startLogin();

The logs:

action:  {
  type: 'LOGIN',
  payload: { code: '1' },
  meta: { resolve: [Function (anonymous)], reject: [Function (anonymous)] }
}
response:  { error: null, data: 'login success' }
setPublicKey
Related