Reputation: 99
I'm using React/Redux for an app I'm building and I'm trying to implement an action to log out the user. My app has artifacts that can be edited. I want my app to first save the artifact, then log out the user, which will clear the store. I've implemented this as follows using Redux Thunk:
export function logoutUser(artifact) {
return (dispatch) => {
dispatch(saveArtifact(artifact))
.then(() => {
dispatch(requestLogout());
return axios.get(`${API_BASE}/auth/logout`)
.then(() => dispatch(logoutSuccess()))
.catch(error => dispatch(logoutFailure(error)));
});
};
}
I would expect this to save the artifact and only when it's saved, log the user out. My save action calls another action itself which loads the artifacts after they're saved, but that shouldn't matter. It should return a promise and only then move on to logging out the user.
However, this is not what is happening. For some reason, I am getting multiple calls to save the artifact (verified they are coming from this function) and then the calls happen asynchronously, so that loading the artifacts is last, and thus my redux store is not getting cleared upon logout.
Would love if someone could provide some insight into why this is happening and how I can fix it.
I am clearing the redux store in my root reducer as follows:
const rootReducer = (state, action) => {
let newState = state;
if (action.type === types.LOGOUT_SUCCESS) {
newState = undefined;
}
return appReducer(newState, action);
};
I am saving the artifacts as follows:
function sendSaveArtifactRequest(artifact) {
if (artifact._id == null) {
const artifactWithoutId = _.omit(artifact, ['_id']);
return axios.post(`${API_BASE}/artifacts`, artifactWithoutId)
.then(result => result.data);
}
return axios.put(`${API_BASE}/artifacts`, artifact).then(() => artifact);
}
export function saveArtifact(artifact) {
return (dispatch) => {
dispatch(requestSaveArtifact());
return sendSaveArtifactRequest(artifact)
.then(data => dispatch(saveArtifactSuccess(data)))
.catch(error => dispatch(saveArtifactFailure(error)))
.then(() => dispatch(loadArtifacts()));
};
}
I am loading artifacts as follows:
function sendArtifactsRequest() {
return new Promise((resolve, reject) => {
axios.get(`${API_BASE}/artifacts`)
.then(result => resolve(result.data))
.catch(error => reject(error));
});
}
export function loadArtifacts() {
return (dispatch) => {
dispatch(requestArtifacts());
return sendArtifactsRequest()
.then(data => dispatch(loadArtifactsSuccess(data)))
.catch(error => dispatch(loadArtifactsFailure(error)));
};
}
Upvotes: 2
Views: 2747
Reputation: 8102
I have found the issue, there are multiple chained promises in your saveArtifact
action. What is happening in your case, after sendSaveArtifactRequest
promise resolution you dispatch requestLogout
and upon it's success you clear redux state. But on the other side there are still pending promises you're not waiting for their resolution in logoutUser
action. When you clear redux store you pending promise gets resolved and the update redux state again.
You can do two things here:
Promise.all().then()
while dispatching saveArtifact
then dispatch logout action.loadArtifacts
action but I'm not sure exact use case, maybe this doesn't fit with your app requirement. but it will always called when artifacts are loaded.Most importantly, please integrate Redux DevTools in your app so you can track in which sequence actions are dispatched.
in your store.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const enhancer = compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
);
export default createStore(rootReducer, enhancer);
`
Upvotes: 1