Использование CreateBrowserRouter из React Router с хранилищем Redux

Я пытаюсь реализовать как Redux, так и React-Router в проекте блога React, в котором я получаю данные из API и сохраняю их в Redux, однако они не отображаются и ошибок не возникает.

Я попробовал следующий код в своем 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>
} 

🤔 А знаете ли вы, что...
JavaScript позволяет создавать собственные серверные приложения с использованием платформы Node.js.


1
291
1

Ответ:

Решено

Функция загрузчика маршрута принимает один объект аргумента со свойствами params и request. arg.post не определено.

Компонент App также должен вернуть JSX, который он собирается отобразить.

Если вам нужно получить доступ к контексту хранилища Redux в загрузчике маршрутов, я бы предложил просто импортировать store и получить доступ непосредственно в функции загрузчика вместо закрытия экземпляра хранилища в маршрутизаторе. Проблема с имеющимся у вас кодом заключается в том, что он будет создавать новый маршрутизатор каждый раз, когда обновляется магазин и выполняется повторная отрисовка компонента Provider.

Пример:

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

Если вы действительно хотите по-прежнему использовать функцию для получения маршрутизатора, то хотя бы запомните маршрутизатор, чтобы он предоставлялся как стабильная ссылка и не создавался заново каждый раз при App или Provider повторном рендеринге.

export default function App() {
  const router = React.useMemo(() => getRouter(store), [store]);

  return (
    <Provider store = {store}>    
      <RouterProvider router = {router} />
    </Provider>
  );
}

Если вам не нужно использовать загрузчик маршрутов, кроме как для доступа и возврата значения хранилища, есть лучшие альтернативы. Вы можете просто использовать хуки useDispatch и useSelector, экспортированные из react-redux, в компонентах, которым необходимо подписаться на обновления состояния.

Пример:

...
import { useSelector } from 'react-redux';
...

const PostList = () => {
  const posts = useSelector(state => state.posts);

  ...

};
const router = createBrowserRouter(
  createRoutesFromElements(
    <Route element = {<Layout />}>
      <Route
        path = "/"
        element = {<PostList />}
        errorElement = {<Error />}
      />
      <Route path='/posts' element = {<PostForm />}>
        ...
      </Route>
      <Route path = "*" element = {<NotFound />} />
    </Route>
  )
)

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

Основное преимущество использования перехватчика useSelector заключается в том, что это подписка: при обновлении выбранного состояния подписанные компоненты будут запускаться для повторной визуализации с текущим значением состояния. Это не тот случай, когда используется store.getState, он получает текущее значение только один раз, в момент вызова getState, и не подписывается на будущие изменения.