为什么redux State只在注销和登录时更新,而不是在从fetchBaseQueryReAuth调用第二个API调用时更新?
第二次API调用在第一次失败后的第二次尝试时成功,但reduxState不会真实的更新状态,只有当我再次登录时才更新?
下面是我的apiSlice:
export const apiSlice = createApi({
baseQuery: baseQueryWithReauth,
endpoints: builder => ({
saveProduct: builder.mutation({
query: (productId) => ({
url: `http://localhost:3005/product/${productId}/save`,
method: 'PUT',
}),
transformResponse : (response) => {
const data = response
console.log(data); // log the response data
return data;
}
})
})
});
当第一个API调用工作时,状态会正常更新,但我仍然无法理解为什么在第二个api调用被调用后,它没有更新,并且在幕后成功更新了我的mongodb和reduxState?
下面是我在第一个API调用工作时使用的dispatch操作:
dispatch(saveProduct(response.data.productId));
下面是客户端上的处理程序触发API调用并更新reduxState:
const [saveProduct, { isLoading, data }] = useSaveProductMutation();
const handleSaveProduct = (productId) => {
saveProduct(productId).unwrap().then((data) => {
// Handle successful response
console.log(data)
}).catch((error) => {
// Handle error
})
}
const savedProductHandler = () => {
if (reduxUser && reduxUser.savedProducts.includes(productId)) {
dispatch(unSaveProductAction(productId));
} else {
dispatch(saveProductAction(productId));
handleSaveProduct(productId);
}
}
为了给予更多的上下文,当产品成功保存并且第一次API调用工作时redux状态正确更新时,保存按钮应该切换到Unsave。
然而,如果第一个API调用失败,第二个调用成功,redux状态不会更新为登录用户,直到我注销并再次登录。保存按钮不会切换到UnSave,当我再次单击Save时,它显示一个错误,Product已经保存。下面是负责此的客户端代码:
{reduxUser && isLoggedIn && reduxUser.savedProducts.includes(productId) ? (
<Button bg="green" color="white" onClick={savedProductHandler}>Unsave</Button>
) : (
<Button bg="green" color="white" onClick={savedProductHandler}>Save</Button>
)}
我已经尝试通过在前端调度useSaveProductMutation()的数据来解决这个问题,但是redux状态仍然没有更新,直到我注销并再次登录。然后,向我显示从第二次成功的API调用中保存的产品。
我也试过用extraReducers来更新状态,但还是没有成功,只有当我注销并重新登录时,状态才会继续更新。
export const apiSlice = createApi({
baseQuery: baseQueryWithReauth,
endpoints: builder => ({
saveProduct: builder.mutation({
query: (productId) => ({
url: `http://localhost:3005/product/${productId}/save`,
method: 'PUT',
}),
transformResponse : (response) => {
const data = response.productId
console.log(data); // log the response data
return data;
},
extraReducers: builder => {
builder.addCase(saveProduct.fulfilled, (state, action) => {
// Update the auth state with the saved product data
console.log('reducer is working')
const savedProduct = action.payload;
state.auth.user.savedProducts.push(savedProduct);
});
}
}),
})
});
我也试过像这样的extraReducers,它没有工作。
const authSlice = createSlice({
name: 'auth',
initialState: {
user: null,
token: null,
refreshToken: null, // add new refreshToken state property
products: [],
savedProducts: [],
},
reducers: {
saveProduct(state,action) {
const savedProduct = action.payload;
state.user.savedProducts.push(savedProduct);
},
},
extraReducers: (builder) => {
builder
.addCase(addProduct.fulfilled, (state, action) => {
const newProduct = action.payload;
state.products.push(newProduct);
})
.addCase(apiSlice.endpoints.saveProduct.fulfilled, (state, action) => {
const savedProduct = action.payload;
state.user.savedProducts.push(savedProduct);
})
},
我必须注销并再次登录才能看到我的savedProduct的问题是我的baseQueryWithReauth函数。我做错的是将我的auth切片的令牌状态和用户状态设置为从我的服务器检索的newAccessToken沿着userId来签署newAccessToken。
最后发生的是,当运行setUser和setToken时,会创建一个新用户,而不是更新。我知道这一点,因为我的Avatar图像是空白的,并且当state.auth.user是console logged时,它只是从baseQueryWithReauth设置的userId字符串,而不是我登录时显示的authSlice的所有不同状态。此外,当我console logged state.auth.user.savedProducts时,在baseQueryWithReauth中设置用户和令牌后,未定义。
另外,我知道这一点是因为当我以用户身份登录时,setUser和setToken被调度。
当第二个API调用成功运行时,state.auth.user.savedProducts在后台更新,只有在我再次登录时才更新。
如何调整我的baseQueryWithReAuth,以便在登录时将state.auth.token和/或state.auth.user更新为我的authSlice?或者编写updateToken或updateUser reducer函数可以解决这个问题吗?
对于上下文,下面是baseQueryWithReauth:
const baseQuery = fetchBaseQuery({
baseUrl: 'http://localhost:3005',
credentials: 'include',
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token
console.log(token)
if (token) {
headers.set("Authorization", `Bearer ${token}`)
}
return headers
}
})
const baseQueryWithReauth = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions)
console.log(result);
if (result?.error?.originalStatus === 403) {
console.log('sending refresh token')
// send refresh token to get new access token
const refreshResult = await baseQuery('/refresh-token', api, extraOptions)
if (refreshResult?.data) {
console.log(refreshResult);
const token = refreshResult.data.token
const user = refreshResult.data.userId
api.dispatch(setToken(token))
api.dispatch(setUser(user))
localStorage.setItem("accessToken", token)
// retry the original query with new access token
result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(logout())
}
}
return result
}
以下是我的authSlice:
const authSlice = createSlice({
name: 'auth',
initialState: {
user: null,
token: null,
savedProducts: [],
},
reducers: {
setToken(state, action) {
state.token = action.payload;
},
setUser(state, action) {
state.user = action.payload;
console.log('userId set:', state.user);
},
saveProduct(state,action) {
const savedProduct = action.payload;
state.user.savedProducts.push(savedProduct);
},
}
1条答案
按热度按时间olmpazwi1#
你真的不应该在
transformResponse
中添加额外的逻辑。该函数应该是纯函数,并且不能保证RTK Query将来不会执行20次,或者如果响应与上次相同,则跳过执行。这不太可能,但你明白我的观点:这个功能是有目的的,副作用不是目的。副作用应该包含在生命周期事件中--在您的情况下,您可以在onQueryStarted中等待,直到查询成功完成,然后分派您的操作。