React Router v6中如何向路由状态传递函数?
问题描述
点击跳转到NewUser路由时,希望在Users和NewUser两个路由间共享状态修改逻辑(setUsers)。通过Link的state传递字符串能正常访问,但传递函数时在NewUser组件中拿到的是null。不想使用Context或Redux,希望通过路由跳转的方式实现。
相关代码
Users组件
function Users() { const [users, setUsers] = useState([]); return ( <Card sx={{ padding: "2rem", mt: "2rem" }}> <MDBox display="flex" flexDirection="row" justifyContent="space-between" > <MDTypography variant="body2">{`You currently have ${users.length} users`}</MDTypography> <MDButton variant="gradient" color="info" size="small"> <Link to="/settings/users/new-user" state={{ setUsers: setUsers }}> <MDBox display="flex" alignItems="center" color="white" fontWeight="normal" > <Icon>add</Icon> Add New User </MDBox> </Link> </MDButton> </MDBox> </Card> ); }
NewUser组件
function NewUser({history}) { const location = useLocation(); const navigate = useNavigate(); const saveChanges = (e) => { location.state.setUsers({ fname: values.firstName, lname: values.lname, email: values.email, }); navigate("/settings/users"); }; return( <MDBox py={3} mb={20} height="62vh"> <Grid container justifyContent="center" alignItems="center" sx={{ height: "100%", mt: 0 }} > <Grid item xs={12} lg={12}> <Formik initialValues={initialValues} validationSchema={currentValidation} onSubmit={(values) => { setValues(values); }} > {({ values, errors, touched, isSubmitting }) => ( <Form id={formId} autoComplete="off"> <Card sx={{ height: "100%", width: "100%" }}> <MDBox px={3} py={4}> <MDBox display="flex"> <ButtonWrapper fullWidth={false} handleClick={saveChanges} > Save Changes </ButtonWrapper> </MDBox> <MDBox> {getStepsContent({ values, touched, formField, errors, })} </MDBox> </MDBox> </Card> </Form> )} </Formik> </Grid> </Grid> </MDBox> ) }
路由配置
[ { type: "collapse", name: "Settings", key: "settings", icon: <Icon fontSize="small">settings</Icon>, collapse: [ { name: "Users", key: "users", route: "/settings/users", component: <Users />, }, { name: "Companies", key: "companies", route: "/settings/companies", component: <Companies />, }, { name: "Billing", key: "billing", route: "/settings/billing", component: <Billing />, }, { name: "Integrations", key: "integrations", route: "/settings/integrations", component: <Integrations />, }, ], }, { name: "New User", key: "new user", route: "/settings/users/new-user", noCollapse: true, component: <NewUser />, }, { type: "collapse", name: "Sign Out", key: "signout", route: "/sign-out", icon: <Icon fontSize="small">logout</Icon>, component: <SignOut />, noCollapse: true, }, ];
路由渲染函数
const getRoutes = (allRoutes) => allRoutes.map((route) => { if (route.collapse) { return getRoutes(route.collapse); } if (route.route) { return <Route exact path={route.route} element={route.component} key={route.key} />; } return null; }); <Routes> {getRoutes(routes)} <Route path="*" element={<Console />} /> </Routes>
问题原因
React Router的Link组件传递的state会被序列化为JSON格式,存储在浏览器的历史记录栈中。而函数属于不可序列化的数据,无法被JSON转换,因此传递后会被浏览器自动丢弃,最终在目标路由中拿到null。
解决方案
方案1:将NewUser设为Users的子路由,通过Outlet传递上下文
这种方式可以直接将setUsers传递给子路由组件,无需依赖路由state。
- 修改路由配置,把
NewUser设为Users的子路由:
{ type: "collapse", name: "Settings", key: "settings", icon: <Icon fontSize="small">settings</Icon>, collapse: [ { name: "Users", key: "users", route: "/settings/users", component: <Users />, collapse: [ { name: "New User", key: "new user", route: "/settings/users/new-user", component: <NewUser />, }, ], }, // 其余配置不变 ], },
- 更新Users组件,使用
Outlet渲染子路由并传递setUsers:
import { Outlet, Link } from 'react-router-dom'; function Users() { const [users, setUsers] = useState([]); return ( <> <Card sx={{ padding: "2rem", mt: "2rem" }}> <MDBox display="flex" flexDirection="row" justifyContent="space-between" > <MDTypography variant="body2">{`You currently have ${users.length} users`}</MDTypography> <MDButton variant="gradient" color="info" size="small"> <Link to="/settings/users/new-user"> <MDBox display="flex" alignItems="center" color="white" fontWeight="normal" > <Icon>add</Icon> Add New User </MDBox> </Link> </MDButton> </MDBox> </Card> {/* 通过Outlet传递上下文 */} <Outlet context={{ setUsers }} /> </> ); }
- 在NewUser组件中通过
useOutletContext获取setUsers:
import { useOutletContext, useNavigate } from 'react-router-dom'; function NewUser() { const { setUsers } = useOutletContext(); const navigate = useNavigate(); const saveChanges = (e) => { // 注意:要往数组里添加新用户,不是直接替换为单个对象 setUsers(prevUsers => [...prevUsers, { fname: values.firstName, lname: values.lname, email: values.email, }]); navigate("/settings/users"); }; // 其余代码不变 }
方案2:将状态提升到路由父组件,通过props传递
把users和setUsers提升到渲染路由的父组件,直接通过props传递给需要的路由组件。
- 修改路由渲染的父组件,添加状态并传递props:
import { useState } from 'react'; import { Routes, Route } from 'react-router-dom'; function AppRoutes() { const [users, setUsers] = useState([]); const getRoutes = (allRoutes) => allRoutes.map((route) => { if (route.collapse) { return getRoutes(route.collapse); } if (route.route) { // 获取组件类型,判断是否需要传递状态 const Component = route.component.type; let componentProps = {}; if (Component === Users || Component === NewUser) { componentProps = { users, setUsers }; } return ( <Route exact path={route.route} element={<Component {...componentProps} />} key={route.key} /> ); } return null; }); return ( <Routes> {getRoutes(routes)} <Route path="*" element={<Console />} /> </Routes> ); }
- 更新Users组件,改用props接收状态:
// 去掉组件内部的useState,改用props传入 function Users({ users, setUsers }) { return ( // 其余代码不变 ); }
- 更新NewUser组件,接收
setUsers:
import { useNavigate } from 'react-router-dom'; function NewUser({ setUsers }) { const navigate = useNavigate(); const saveChanges = (e) => { setUsers(prevUsers => [...prevUsers, { fname: values.firstName, lname: values.lname, email: values.email, }]); navigate("/settings/users"); }; // 其余代码不变 }
内容的提问来源于stack exchange,提问作者Mustafa Yusuf
相关产品推荐
相关产品推荐

