【问题标题】:How to read RTK Query state without hooks in react-router 6.4如何在 react-router 6.4 中读取没有钩子的 RTK 查询状态
【发布时间】:2022-12-15 02:55:37
【问题描述】:

最近新的react-router 6.4 发布了,它有能力在组件渲染之前加载数据。 (https://reactrouter.com/en/main/start/overview#data-loading)

这似乎是一个很酷的功能。我想将它与 RTK 查询一起使用,因为我已经在使用 Redux Toolkit。

所以我想要一个基本的东西,我有 api 来加载帖子。我想加载它们,如果请求失败 - 重定向到其他页面。在react-router 6.4 中,这一切都可以在路由器中完成(https://reactrouter.com/en/main/start/overview#redirects)。

路由器在反应范围之外,所以我不能使用 rtk 查询提供的钩子,所以这意味着我必须使用没有钩子的 rtk 查询,根据文档这是完全可能的(https://redux-toolkit.js.org/rtk-query/usage/usage-without-react-hooks

所以我的问题是,如何读取 react-router 加载程序中的请求状态。我能够使用挂钩读取组件中的状态,但它会使组件“变脏”并在整个应用程序中传播逻辑。

import React from "react";
import ReactDOM from "react-dom/client";

import { createBrowserRouter, RouterProvider } from "react-router-dom";

import { Provider } from "react-redux";
import { store } from "./redux/redux";

import { Comments } from "./Comments";
import { Posts } from "./Posts";
import { Root } from "./Root";

import { postsApi } from "./redux/redux";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
  },
  {
    path: "posts",
    element: <Posts />,
    loader: () => {
      store.dispatch(postsApi.endpoints.getPosts.initiate());

      const request = postsApi.endpoints.getPosts.select()(store);

      console.log(request);

      return request;
    },
  },
  {
    path: "/comments",
    element: <Comments />,
  },
]);

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <Provider store={store}>
    <RouterProvider router={router} />
  </Provider>
);

我正在尝试使用文档中的示例,但我总是在控制台中获得“未初始化”状态,就像从未发出过请求一样,但我可以在 redux 开发工具中看到数据。而且我还收到错误消息“在state.postsApi 找不到数据。您是否忘记将减速器添加到商店?”

【问题讨论】:

    标签: reactjs react-router react-router-dom rtk-query


    【解决方案1】:

    你可能会做一些事情

    const promise = dispatch(api.endpoints.myEndpoint.initiate(someArgument))
    await promise // wait for data to be there
    promise.unsubscribe() // remove the subscription. The data will stay in cache for 60 seconds and the component can subscribe to it in that timeframe.
    

    请注意,我不访问此处的数据,您可能也不应该访问。 虽然它将在 await promise 之后可用,但我只会将加载程序用于存在的数据 - 然后在组件中使用正常的 useMyEndpointQuery(someArgument) 来访问该数据。

    您需要使用挂钩,以便缓存知道您的组件实际上正在使用该数据 - 否则它将在 60 秒后从缓存中删除(或者如果您从未取消订阅,它将永远不会被删除)。

    到那时,将数据从 loader 函数传递到组件中并没有真正的好处——无论如何它已经能够通过钩子访问它。

    所以我的建议是:启动并等待从 loader 获取数据,但实际上像以前一样从组件访问数据。

    【讨论】:

    • 谢谢你的回答。所以没有办法在加载器中重定向,对吧?我只是觉得这很不方便,我们试图首先显示一个组件,然后才获取数据,如果数据加载失败,我们将重定向到其他页面。如果没有数据,显示组件有什么意义?
    • 所以对于任何正在寻找的人。在这上面花半天时间,我觉得有点傻。我通过这样做解决了我的问题:我将查询保存到变量中:const request = store.dispatch(postsApi.endpoints.getPosts.initiate());然后我为它await。然后我通过点符号 request.isError 获得了所有需要的对象键。所以现在我在路由加载器中发出请求,如果它失败我进行重定向,如果一切正常,我使用 rtk 查询挂钩在组件中使用接收到的数据。奇迹般有效!
    • @Yastrebtsov 我正试图在我的项目中实现类似的目标,你是否有机会使用代码沙箱/JSFiddle 来展示你是如何解决这个问题的?谢谢!
    最近更新 更多