Ad

How Do I Make Sure That Reducer Response Action Is Done Before Navigating To Another Page?

- 1 answer

I'm currently using Redux Saga with React Native and I have a question on how to properly handle the situation where the reducer action is not done firing a success or fail action before I navigate to another page. I need to wait until the success or fail action is fired before navigating to another page but it navigates anyway.

I set up my code so that it checks for loading or error state before navigation but navigation still happens anyway. I think it's because updateItems is async and the proceeding codes executes so quickly before the response comes back e.g. in the event that I do get an error, the page navigates away instead of waiting so that I can display the error on the page.

I think it might be related to something I need to add in my reducer but I'm not sure.

Here's the method that I click on that triggers the reducer call and the navigation is all is well.

updateItems = () => {
    const { code } = this.state;
    const { loading, error } = this.props;
    const id = this.props.navigation.state.params.id;

    this.props.updateItems({
      id: id,
      code: code,
    });

    if (!loading && !error) {
      this.props.navigation.navigate("newPage");
    }
  };

Here's the reducer itself:

const UPDATE_ITEMS_REQUEST = "UPDATE_ITEMS_REQUEST";
const UPDATE_ITEMS_SUCCESS = "UPDATE_ITEMS_SUCCESS";
const UPDATE_ITEMS_FAILURE = "UPDATE_ITEMS_FAILURE";

export default (itemsReducer = (
  state = {
    data: {
      items: {},
    },
    error: null,
    loading: false,
  },
  { type, payload },
) => {
  switch (type) {
    case UPDATE_ITEMS_REQUEST:
      return Object.assign({}, state, { loading: true, error: null });
    case UPDATE_ITEMS_FAILURE:
      return Object.assign({}, state, { loading: false, error: payload });
    case UPDATE_ITEMS_SUCCESS:
      return {
        loading: false,
        error: null,
        data: payload,
      };
    default:
      return state;
  }
});

export function updateItems(payload) {
  return { type: UPDATE_ITEMS_REQUEST, payload };
}

export function updateItemsFailure(payload) {
  return { type: UPDATE_ITEMS_FAILURE, payload };
}

export function updateItemsSuccess(payload) {
  return { type: UPDATE_ITEMS_SUCCESS, payload };
}


function* updateItemsSaga(payload) {
  const response = yield call(putItems, payload);
  if (response.ok) {
    yield put(updateItemsSuccess(payload));
  } else {
    yield put(
      updateItemsFailure("There has been an error"),
    );
  }
}

export function* watchUpdateItems() {
  yield takeLatest(UPDATE_ITEMS_REQUEST, updateItemsSaga);
}

// API
const putItems = async payload => {
  const res = await fetch(
    `${env.url}/updateItems`,
    {
      method: "PUT",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        id: payload.payload.id,
        code: payload.payload.code,
      }),
    },
  );

  if (!res.ok)
    return {
      status: res.status,
      ok: false,
    };

  const response = await res.json();
  response.status = res.status;
  response.ok = res.ok;
  return response;
};
Ad

Answer

It seems that the issue is located in updateItems. It have an internal scope with two properties { loading, error }. Since this is not a generator function, As soon as this function starts executing, an action updateItems is fired (which is fine), and right after that the navigation happens because the function checks { loading, error } (which are false & null accordingly).

The solution can be using the navigation part inside updateItemsSaga like so:

function* updateItemsSaga(payload) {
  const response = yield call(putItems, payload);
  if (response.ok) {
    yield put(updateItemsSuccess(payload));
    this.props.navigation.navigate("newPage");
  } else {
    yield put(
      updateItemsFailure("There has been an error"),
    );
  }
}

Alternatively, you can extract the condition from updateItems and use it in your component itself:

const OldPage = ({ loading, error }) => {

    if (loading) {
        return <div>Loading... </div>
    } else if(error) {
        return <ErrorPage status="404">;
    } else {
        return <NewPage>;
    }

};

Both should stop the navigation from happening, until items have updated successfully. Hope it helps

Ad
source: stackoverflow.com
Ad