React学习时,outlet 路由配置 (prop传参处理,跳转的实现,父子数据共享)
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
App.js文件
import { HashRouter, Routes, Route } from 'react-router-dom';
import Login from './page/login'
import Loginafter from './page/login/loginafter';
import Loginbefore from './page/login/loginbefore';
import Home from './page/home/home'
import About from './page/about/about'
import Integrated from './page/integrated/integrated'
import Sidebar from './page/sidebar/sidebar'
import Latent from './page/latent/latent'
import Particulars from './page/particulars/particulars'
import SecurityCheck from './page/securityCheck/securityCheck'
function App() {
{/* 如果需要默认显示页面将不需要填写路径 ,子路由不需要写/斜杠跳转时会带有*/}
return (
<HashRouter>
<Routes>
<Route path='/' element={<Login />} >
<Route path='' element={<Loginbefore />} ></Route>
<Route path='/Loginafter' element={<Loginafter />} ></Route>
</Route>
<Route path='/home' element={<Home />} ></Route>
<Route path='/about' element={<About />} >
<Route path='' element={<Integrated />} ></Route>
<Route path='sidebar' element={<Sidebar />} >
<Route path='' element={<Latent />} ></Route>
<Route path='particulars' element={<Particulars />} ></Route>
<Route path='securityCheck' element={<SecurityCheck />} ></Route>
</Route>
</Route>
</Routes>
</HashRouter>
);
}
export default App;
父组件案例【about.js】
import React from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
export default function About() {
const navigate = useNavigate();
// let ddd = [{ sss: 2 }, { bbb: 4 }]
let ddd = 1;
const [count, setCount] = React.useState(ddd); //子组件对 setCount 操作后将被此处响应接收
return (
<>
{/* 跳转的同时 重定向默认页 */}
{/* navigate('integrated', {replace: true}); */}
{/* 跳转默认页 */}
{/* navigate('',); */}
{/* 变更父页 '/'+页名 */}
{/* navigate('/home') */}
{/* 跳转同级页 页名 */}
{/* navigate('sidebar') */}
{/* 跳转子页面 页名+'/子页名' */}
{/* navigate('sidebar/securityCheck', { state: ddd }); */}
{/* 实验,页面跳转时传入的参数,会不会随着刷新丢失? */}
{/* 结论: 依旧建议数据为接口返回的,当然,接收页(子组件)收到的数据并不会被刷新掉 */}
{/* 回到上一页 */}
{/* navigate(-1) */}
<div>
<h4>About</h4>
<button onClick={() => {
navigate('test', { state: ddd });
// state 传参不限制传参类型
}}>点击Test页</button>
</div>
{/* 以下为渲染子页面 */}
<Outlet context={[count, setCount]} />
</>
)
}
子组件案例【test.js】
import React, { Component } from 'react'
import { useOutletContext } from 'react-router-dom';
// outlet 状态共享
// 无状态子组件自身发生数据变化,同步给outlet上游组件 本文件以此实现数据共享
const Test = () => {
const [count, setCount] = useOutletContext();
console.log('count',count) //打印父数据
//useOutletContext 路由内置方法,监听数据发生变动
//数据类型任意 由上游 父组件决定
const increment = () => setCount((c) => c + 1); //操作父数据
return <button onClick={increment}>{count}</button>;
}
export default Test
即使outlet 共享了数据/状态,子组件初次渲染也拿到了,但超出局面的状况,可能是由于我有部分知识未能掌握,又或者由其他原因造成的。
查阅了网上一些博主的做法后,我尝试了使用 useEffect 异步更新,然并软。
this.forceUpdate() 强制更新,毕竟我实操变更后数据后,父组件成功打印了它。
然并软。(我猜测可能是子组件为无状态组件缘故,无生命周期导致数据变更的第一时间发生了请求...可以尝试用构造类组件写,当然useOutletContext只支持无状态组件内使用...)
有位博主的做法是,判定虚拟dom元素创建前,获取路由路径,这种做法挺高明的,我也是打算用一则案例写跳转时token的判定机制,和他的这种殊道同归,他也是以一种上下文的实现途径,只不过我是以v6的那种,他的写法上配置全局上下文,有点费时间,早期写过但版本不一致导致我这边报错过,所以我舍弃了旧写法。 但我依旧认为,一定有种很简别的方式实现数据变更,也许只是我没有想到而已...
最终,有一则报错告诉我,我可能需要将数据以数组包对象形式传递给子组件。
于是,我猜测,当我编辑后返回数据时,我可能需要push进去,再删除编辑前的数据,使我的无状态组件渲染时,不至于某行字段报null 或undefined。 至此,附上新案例:
父组件 login 文件夹下的 index.js
import React, { Component, useEffect } from 'react'
import { Outlet, useNavigate, useSearchParams } from 'react-router-dom';
const Btn = () => {
let state = {
token: 'asldfjdljfdsojdakf haiksf',
userName: 'zhangsan',
password: '123456'
}
// let state = 11
const [grossdata, setgrossdata] = React.useState([state])
// useEffect(() => {
// console.log('异步更新', grossdata)//监听数据发生了更改
// console.log('this',this)
// // this.forceUpdate()
// // if (!grossdata.token) {
// // console.log('判断')
// // // grossdata.token = <></>
// // // this.forceUpdate()
// // }
// }, [grossdata])
const navigate = useNavigate();
return <>
<button onClick={() => { navigate('Loginafter') }}>前往登录后</button>
<Outlet context={[grossdata, setgrossdata]} />
</>
}
export class index extends Component {
render() {
return (
<>
<h4>这是管理登录页和登录后页面的文件</h4>
<Btn></Btn>
</>
)
}
}
export default index
子组件 login 文件夹下的 loginafter.js
import React from 'react'
import { useOutletContext } from 'react-router-dom';
const Loginafter = () => {
const [grossdata, setgrossdata] = useOutletContext();
console.log('grossdata子组件打印', grossdata)
const increment = () => setgrossdata((c) => {
c[0].token = '';
let f = c[0]
return c.push(f) && c.splice(0, 1)
}); //操作父数据
return <>
<div>登录后</div>
<button onClick={increment}>清除token</button>
<br></br>
<span>{grossdata[0].token} </span>
<br></br>
<span>{grossdata[0].userName}</span>
<br></br>
<span>{grossdata[0].password}</span>
</>
}
export default Loginafter
补充:
对 useOutletContext 接收的数据,我起初只想着用,但在为对象做变更时报错,以致发现自己的无知,这使我做了许多无用功:this.forceUpdate() 强制更新, useEffect 异步更新 。
瞅下官网的说法:

分类:
React
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构