React-Router V7中如何向createBrowserRouter传递props及替代方案咨询
兄弟,你现在的代码确实存在问题哦——你把createBrowserRouter定义在App组件外部,而articles是App内部的useState状态,router初始化的时候根本拿不到这个状态值,就算后续articles更新了,router也不会同步感知到,所以你现在给Home组件传的articles其实是无效的。下面给你几个可行的解决方案,你可以根据自己的场景选择:
方案一:将Router移到App组件内部,用useMemo缓存
把router的创建逻辑放到App组件里,这样就能直接访问到articles状态了,同时用useMemo来缓存router实例,避免每次App渲染都重新创建router,影响性能:
import { useState, useMemo } from 'react'; import { createBrowserRouter, createRoutesFromElements, Route, RouterProvider } from 'react-router-dom'; export function App() { const [articles, setArticles] = useState(); // 用useMemo缓存router,只有当articles变化时才重新创建 const router = useMemo(() => createBrowserRouter( createRoutesFromElements( <Route path="/" element={<Layout />}> <Route index element={<Home articles={articles} />} /> <Route path="About" element={<About />} /> </Route> ) ), [articles] // 依赖项数组,articles变化时更新router ); return ( <div> <RouterProvider router={router} /> </div> ); }
这个方案简单直接,不需要额外的状态管理,适合状态只在当前路由树里用到的场景。唯一的小缺点是如果你的路由配置很复杂,放在组件里会让App组件的代码显得有点臃肿。
方案二:使用React Context(你提到的方案)
如果articles是多个组件都需要用到的全局状态,用Context是个不错的选择。具体步骤如下:
步骤1:创建Context
// 可以单独放在一个文件里,比如ArticleContext.js import { createContext, useContext } from 'react'; const ArticleContext = createContext(); export function ArticleProvider({ children, articles }) { return ( <ArticleContext.Provider value={articles}> {children} </ArticleContext.Provider> ); } export function useArticles() { return useContext(ArticleContext); }
步骤2:在App里用Provider包裹Router
import { useState } from 'react'; import { createBrowserRouter, createRoutesFromElements, Route, RouterProvider } from 'react-router-dom'; import { ArticleProvider } from './ArticleContext'; // router可以继续放在外面,因为不需要直接访问articles了 const router = createBrowserRouter( createRoutesFromElements( <Route path="/" element={<Layout />}> <Route index element={<Home />} /> {/* 这里不用传props了 */} <Route path="About" element={<About />} /> </Route> ) ); export function App() { const [articles, setArticles] = useState(); return ( <ArticleProvider articles={articles}> <RouterProvider router={router} /> </ArticleProvider> ); }
步骤3:在Home组件里获取数据
import { useArticles } from './ArticleContext'; function Home() { const articles = useArticles(); // 接下来就可以正常使用articles了 return ( <div> {articles?.map(article => ( <div key={article.id}>{article.title}</div> ))} </div> ); }
这个方案适合需要在多个组件(比如Layout、其他路由组件)里共享articles的场景,缺点是需要多写一点Context的样板代码,但胜在扩展性强,后续新增需要用articles的组件时,直接用useArticles就能拿数据。
方案三:使用React Router的Loader函数(适合数据从服务端获取的场景)
如果你的articles是从API获取的,或者可以通过路由的loader来提供数据,那可以用React Router的loader功能,Home组件通过useLoaderData来获取数据,完全不需要传props:
步骤1:配置路由的loader
const router = createBrowserRouter( createRoutesFromElements( <Route path="/" element={<Layout />}> <Route index element={<Home />} loader={async () => { // 这里可以写获取articles的API请求逻辑 const res = await fetch('/api/articles'); return res.json(); }} /> <Route path="About" element={<About />} /> </Route> ) );
步骤2:在Home组件里获取数据
import { useLoaderData } from 'react-router-dom'; function Home() { const articles = useLoaderData(); return ( <div> {articles.map(article => ( <div key={article.id}>{article.title}</div> ))} </div> ); }
这个方案符合React Router的设计理念,路由级别的数据由路由自己管理,缺点是如果articles是客户端的本地状态(比如用户在页面上添加/修改的文章),那loader就不太适用了,更适合服务端获取的静态数据。
备注:内容来源于stack exchange,提问作者adam

