问题
When I get 401 status code from backend I run refreshToken method with passing the function where expired token occurred. In refreshToken method I get new token and set in refreshTokenLastFunc property function from parameter.
Then I watch when refreshTokenLastFunc was updated using React useEffect and run once again the function where expired token occurred.
The problem is while I run store.refreshTokenLastFunc() in useEffect, the function in refreshTokenLastFunc property uses old Context API store(so it uses old token not the new one). You can read my comment in useEffect for store.refreshTokenLastFunc.
export const StoreProvider = props => {
const getToken = () => localStorage.getItem("token");
const initState = () => ({
token: getToken(),
isAuth: false,
userRole: "old role",
mainUrl: MainUrl,
apiUrl: ApiUrl,
refreshTokenLastFunc: () => {}
})
const [store, setStore] = useState(initState());
const getUserInfo = async () => {
if (getToken()) {
try {
const apiConfig = {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${store.token}`,
},
};
const response = await fetch(`${store.apiUrl}get-me`, apiConfig);
const responseJson = await response.json();
if (response.ok) {
// Update Context API
setStore({
...store,
userRole: responseJson.role,
userName: responseJson.name,
userGroupId: responseJson.group_id,
isAuth: true,
})
} else if(response.status === 401) {
refreshToken(getUserInfo);
} else {
throw new Error(`Some error occurred`);
}
} catch (error) {
console.log(error);
}
}
}
const refreshToken = async func => {
try {
const apiConfig = {
method: "GET",
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${store.token}`,
},
};
const response = await fetch(`${store.mainUrl}refresh-token`, apiConfig);
const responseJson = await response.json();
if (response.ok) {
// Update token in local storage
localStorage.setItem("token", responseJson.token);
// Update Context API
setStore({
...store,
userRole: 'new role',
token: responseJson.token,
refreshTokenLastFunc: func,
})
} else {
throw new Error(`Some error...`);
}
} catch (error) {
throw error;
}
}
useEffect(() => {
getUserInfo();
}, []);
useEffect(() => {
// If I console log my store before calling function, store is correctly updated, but the function uses old store.
console.log('store from useEffect: ', store); // store.userRole = 'new role' which is correct
store.refreshTokenLastFunc(); // store.userRole = 'old role' which should be 'new role'
}, [store.refreshTokenLastFunc]);
return(
<StoreContext.Provider value={[store, setStore, logout, getUserInfo]}>
{props.children}
</StoreContext.Provider>
);
}
来源:https://stackoverflow.com/questions/59488943/function-passed-as-parameter-takes-old-store-from-react-context-api