skip to Main Content

I am trying to implement both Redux and React-Router in a React blog project in which I fetch data from an API and store in Redux, however, it is not being rendered and no error is given.

I tried the following code in my App.jsx

const getRouter = (store) => {
  return (
    createBrowserRouter(
      createRoutesFromElements(
        <Route path="/" element={<Layout />}>
          <Route
            index
            element={<PostList />}
            errorElement={<Error />}
            loader={(arg) => {
              arg.post = store.getState().posts
            }}
          />
          <Route  path='/posts' element={<PostForm />}>
            <Route  />
          </Route>
          <Route path="*" element={<NotFound />} />
        </Route>
      )
    )
  )
}

export default function App() {
  <Provider store={store}>
    <RouterProvider router={getRouter(store)} />
  </Provider>
} 

2

Answers


  1. Using createBrowserRouter and createRoutesFromElements: These functions are not standard React Router functions. Typically, you would use BrowserRouter from react-router-dom and define routes using JSX directly, without helper functions like createRoutesFromElements.

    const App = () => {
      return (
        <Provider store={store}>
          <Router>
            <Routes>
              <Route path="/" element={<Layout />}>
                <Route index element={<PostList />} />
                <Route path="/posts" element={<PostForm />} />
                <Route path="*" element={<NotFound />} />
              </Route>
            </Routes>
          </Router>
        </Provider>
      );
    };
    
    export default App;
    Login or Signup to reply.
  2. The route loader function accepts a single argument object with params and request properties. arg.post isn’t defined.

    The App component also needs to return the JSX it is intending to render.

    If you need to access the Redux store context in a route loader then I’d suggest just importing store and accessing directly in the loader function instead of closing over an instance of the store in the router. The issue with the code you have is that it will create a new router each time the store updates and the Provider component rerenders.

    Example:

    const router = createBrowserRouter(
      createRoutesFromElements(
        <Route element={<Layout />}>
          <Route
            path="/"
            element={<PostList />}
            errorElement={<Error />}
            loader={({ params, request }) => {
              const { posts } = store.getState();
              // return some value to `PostList` component
            }}
          />
          <Route path='/posts' element={<PostForm />}>
            ...
          </Route>
          <Route path="*" element={<NotFound />} />
        </Route>
      )
    )
    
    export default function App() {
      return (
        <Provider store={store}>
          <RouterProvider router={router} />
        </Provider>
      );
    }
    

    If you really wanted to still use a function to get the router then at least memoize the router so it’s provided as a stable reference and not re-created each time App or Provider rerender.

    export default function App() {
      const router = React.useMemo(() => getRouter(store), [store]);
    
      return (
        <Provider store={store}>    
          <RouterProvider router={router} />
        </Provider>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search