如何从rtk查询更新redux状态?

4ngedf3f  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(134)

为什么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);
    },
}
olmpazwi

olmpazwi1#

你真的不应该在transformResponse中添加额外的逻辑。该函数应该是纯函数,并且不能保证RTK Query将来不会执行20次,或者如果响应与上次相同,则跳过执行。这不太可能,但你明白我的观点:这个功能是有目的的,副作用不是目的。
副作用应该包含在生命周期事件中--在您的情况下,您可以在onQueryStarted中等待,直到查询成功完成,然后分派您的操作。

相关问题