How Do I Make Sure That Reducer Response Action Is Done Before Navigating To Another Page?
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;
};
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
Related Questions
- → How to update data attribute on Ajax complete
- → October CMS - Radio Button Ajax Click Twice in a Row Causes Content to disappear
- → Octobercms Component Unique id (Twig & Javascript)
- → Passing a JS var from AJAX response to Twig
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → DropzoneJS & Laravel - Output form validation errors
- → Import statement and Babel
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM