如何在react-router 6.4中不使用hooks读取RTK查询状态

Yas*_*sov 15 reactjs react-router react-router-dom rtk-query

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

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

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

路由器超出了react的范围,所以我不能使用rtk查询提供的钩子,所以这意味着我必须使用没有钩子的rtk查询,根据文档这是完全可能的(https://redux-toolkit.router)。 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>
);
Run Code Online (Sandbox Code Playgroud)

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

Fab*_*ank 8

您在这里的方向是正确的,但是您需要对调度返回的 Promise 调用unwrap(),如果成功则返回结果,否则抛出异常。我修改了你的代码片段以表达我的意思。

    loader: () => {
      const p = store.dispatch(postsApi.endpoints.getPosts.initiate());

      try {
        const response = await p.unwrap()
        return response
      } catch (e) {
        // see https://reactrouter.com/en/main/fetch/redirect
        return redirect("/login")
      } finally {
        p.unsubscribe()
      }
    },
Run Code Online (Sandbox Code Playgroud)


phr*_*hry 5

你可能会做一些类似的事情

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.
Run Code Online (Sandbox Code Playgroud)

请注意,我不会访问此处的数据,您可能也不应该访问。虽然在那之后它将可用await promise,但我只会使用加载器来呈现数据 - 然后使用useMyEndpointQuery(someArgument)组件中的法线来访问该数据。

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

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

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

  • 所以对于任何正在寻找的人来说。我觉得自己花了半天时间在这个问题上有点愚蠢。我通过这样做解决了我的问题:我将查询保存到变量中: `const request = store.dispatch(postsApi.endpoints.getPosts.initiate());` 然后我 `await` 。然后我通过点符号“request.isError”获得了所有需要的对象键。所以现在我在路由加载器中发出请求,如果失败,我会进行重定向,如果一切正常,我会使用 rtk 查询挂钩在组件中使用收到的数据。奇迹般有效! (3认同)