Search code examples
reactjsreact-routerreact-router-domrtk-query

How to read RTK Query state without hooks in react-router 6.4


Recently new react-router 6.4 was released and it has the ability to load data before render of component. (https://reactrouter.com/en/main/start/overview#data-loading)

This seems like a cool feature. And I want to use it with RTK Query, since I already use Redux Toolkit.

So I want to to a basic thing, I have the api to load posts. I want to load them, and if request fails - redirect to other page. In react-router 6.4 it all can be done in router(https://reactrouter.com/en/main/start/overview#redirects).

Router is outside of scope of react, so I can not use hooks which are provided by rtk query, so it means that I have to use rtk query without hooks, which according to documentation is totally possible (https://redux-toolkit.js.org/rtk-query/usage/usage-without-react-hooks)

So my problem is, how do I read status of the request IN the react-router loader. I am able to read status in components, using hooks, but it makes components "dirty" and spreads the logic across the app.

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>
);

I am trying to use example from the docs, but I always get status "uninitialized" in the console, like the request had never been made, but it was, I can see the data in redux dev tools. And I also get the error "No data found at state.postsApi. Did you forget to add the reducer to the store?"


Solution

  • You are on the right track here, but you need to call unwrap() on the promise returned by dispatch, which will either return the result if successful or throw if not. I've modified your snippet to show what I mean.

        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()
          }
        },