I'm making a component where users can change their details, and the way I'm doing it is that they are essentially deleting their account and remaking it - doing this with Redux and JWT. If there's a better way of doing this, please let me know, but for now this is doing the trick for me.
When a user first creates an account, it sets their JWT token with the correct details in the local storage. That works fine. It also sets it no problem when they log in.
The problem is that when they call the "edit" function, which deletes their account and creates a new one, the above is not happening. So if I create an account with username "aaa", the application shows that with the key of profile and the values I created. But when I call edit it - to username "bbb" for example, even though a new account is successfully created (shows up in my MongoDB, can be logged into), it is not updating the token properly - the values for the key are showing as an empty object of {}.
Is there a simple way to clear and then update the token that I am missing? Would love a hand!
My actions are here:
import * as api from "../api";
import { AUTH, DELETE } from "./ActionTypes";
export const signin = (formData, history) => async (dispatch) => {
try {
const { data } = await api.signIn(formData)
dispatch({ type: AUTH, data })
history.push("/");
} catch (error) {
console.log(error);
}
};
export const signup = (formData, history) => async (dispatch) => {
try {
const { data } = await api.signUp(formData)
dispatch({ type: AUTH, data })
history.push("/");
} catch (error) {
console.log(error);
}
};
export const editProfile = (formData, history) => async (dispatch) => {
try {
const { data } = await api.editProfile(formData)
const { data2 } = await api.signUp(formData)
Promise.resolve(dispatch({ type: DELETE, data })).then(
() => dispatch({ type: AUTH, data2 }))
history.push("/profile");
} catch (error) {
console.log("error here", error);
}
};
And my reducers are here:
import { AUTH, LOGOUT, EDIT, REFRESH } from "../actions/ActionTypes";
const authReducer = (state = { authData: null }, action) => {
switch (action.type) {
case AUTH:
localStorage.setItem('profile', JSON.stringify({...action?.data}));
return {...state, authData: action.data};
break;
case LOGOUT:
localStorage.clear()
return {...state, authData: null};
return state;
break;
case EDIT:
localStorage.clear()
return {...state, authData: null};
default:
return state;
break;
}
};
export default authReducer;
My GitHub for the project is here: https://github.com/gordonmaloney/lingr-mern/
(As an aside, I had spent all day trying to work out how I might be able to patch the details before giving up and coming to Stack Overflow to ask, but as I was typing up my question it dawned on me that I could try doing it by just deleting and recreating, and I'm now SO CLOSE to getting it where I want that this last little bump is really frustrating me!)
CodePudding user response:
You have a little bit of Promises vertigo here. Let's explore the editProfile
function, this bit in particular:
Promise.resolve(dispatch({ type: DELETE, data })).then(/* ... */)
I'll quote docs on Promise.resolve()
:
Promise.resolve() method returns a Promise object that is resolved with a given value
And dispatch()
returns a Promise
itself. So, what you're doing here is wrapping a promise which is returned by dispatch
into one more promise, immediately resolve this "outer" promise by calling Promise.resolve()
and then you are dispatching AUTH
action.
What you should do is:
dispatch({ type: DELETE, data })).then(() => dispatch({ type: AUTH, data2 }));
Or, which reads better in my opinion:
await dispatch({ type: DELETE, data });
await dispatch({ type: AUTH, data2 });
history.push("/profile");
Which means you'll wait for dispatch
to finish and then call the second dispatch.
CodePudding user response:
I worked it out!
It was because both reducers were looking for action.data, which is how the sign up action was called originally. But because it was called "data2" when called as part of the edit action, it wasn't finding anything to update the token with. I changed the names and now it works perfectly.
For reference, the new code is below:
Actions:
import * as api from "../api";
import { AUTH, DELETE } from "./ActionTypes";
export const signin = (formData, history) => async (dispatch) => {
try {
const { data } = await api.signIn(formData)
dispatch({ type: AUTH, data })
history.push("/");
} catch (error) {
console.log(error);
}
};
export const signup = (formData, history) => async (dispatch) => {
try {
const { data } = await api.signUp(formData)
dispatch({ type: AUTH, data })
history.push("/");
} catch (error) {
console.log(error);
}
};
export const editProfile = (formData, history) => async (dispatch) => {
try {
const { data2 } = await api.editProfile(formData)
const { data } = await api.signUp(formData)
await dispatch({ type: DELETE, data2 });
await dispatch({ type: AUTH, data });
history.push("/profile");
} catch (error) {
console.log(error);
}
};
Reducers:
import { AUTH, LOGOUT, EDIT, REFRESH } from "../actions/ActionTypes";
const authReducer = (state = { authData: null }, action) => {
switch (action.type) {
case AUTH:
localStorage.setItem('profile', JSON.stringify({...action?.data}));
console.log(action)
return {...state, authData: action.data};
break;
case LOGOUT:
localStorage.clear()
return {...state, authData: null};
break;
case EDIT:
localStorage.clear()
localStorage.setItem('profile', JSON.stringify({...action?.data2}));
return {...state, authData: null};
default:
return state;
break;
}
};
export default authReducer;