此次版本升級(jí)涉及改動(dòng)較多,且不向下兼容
1. Switch組件換為Routes, Route組件的render換為element
// old
import {
Route, Switch, withRouter,
} from 'react-router-dom';
<Switch>
{routes.map(
({path, exact, component: Component, routes: Routes}) => (
<Route
key={path}
path={path}
exact={exact}
render={props => <Component {...props} routes={Routes} />}
/>),
)}
<Route render={props => <NoMatch {...props} />} />
</Switch>
// new
import {
Route, Routes, Navigate,
} from 'react-router-dom';
<Routes>
{routes.map(
({path, component: Component, children}) => (
<Route
key={path}
path={path}
element={<Component {...props} />}
>
// 子路由的寫(xiě)法,之前可以寫(xiě)在組件中的嵌套Switch中
{children && children.map(({ path, component: Component }) => {
if (path === '/wealth/set') {
return (<Route
element={<Navigate to="/wealth/set/mechanismset" />}
path="/wealth/set"
/>)}
return (
<Route
key={path}
path={path}
element={<Component {...props} />}
/>)
})}
</Route> )
)}
<Route render={(props) => <NoMatch {...props} />} />
</Routes>
2. Redirect組件廢棄
// old
if(route.path == '/wealth/set'){
return <Redirect to={tabs[0].key} />
}
// new
<Route
element={<Navigate to={tabs[0].key} />}
path="/wealth/set"
/>
3.子路由的渲染及Outlet的使用
// old直接子組件中放Switch
//App.js
<Switch>
{routes.map(
({path, exact, component: Component, routes: Routes}) => (
<Route
key={path}
path={path}
exact={exact}
render={props => <Component {...props} routes={Routes} />}
/>),
)}
<Route render={props => <NoMatch {...props} />} />
</Switch>
// child.js
<Switch>
{
// App.js 拿到傳過(guò)來(lái)的值,并循環(huán)配置路由
this.props.routes.map((route, key) => {
if (route.path == '/wealth/set') {
return <Redirect to={tabs[0].key} key={key} />
}
return <Route key={key} exact={route.exact} path={route.path} render={(props) => <route.component {...props} />} />
})
}
</Switch>
// new app中嵌套路由,由Outlet做展示
// App.js
<Routes>
{routes.map(
({path, component: Component, children}) => (
<Route
key={path}
path={path}
element={<Component {...props} />}
>
// 子路由的寫(xiě)法,之前可以寫(xiě)在組件中的嵌套Switch中
{children && children.map(({ path, component: Component }) => {
if (path === '/wealth/set') {
return (<Route
element={<Navigate to="/wealth/set/mechanismset" />}
path="/wealth/set"
/>)}
return (
<Route
key={path}
path={path}
element={<Component {...props} />}
/>)
})}
</Route> )
)}
<Route render={(props) => <NoMatch {...props} />} />
</Routes>
// child.js
<Outlet {...this.props} />
4. withRouter廢棄
// 實(shí)現(xiàn)withRouter
import React from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
export default function withRouter(Child) {
return function(props) {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return <Child {...props} navigate={navigate} params={params} location={location} />;
}
}
5. Prompt廢棄
重寫(xiě)B(tài)rowserRouter,替換'react-router-dom'的BrowserRouter
// 需要先給BrowserRouter注入history對(duì)象
import React from 'react';
import { createBrowserHistory } from "history";
import { Router } from "react-router";
const browserHistory = createBrowserHistory({ window });
const BrowserRouter = ({
basename,
children,
window,
}) => {
let historyRef = React.useRef();
if (historyRef.current == null) {
historyRef.current = browserHistory;
}
let history = historyRef.current;
let [state, setState] = React.useState({
action: history.action,
location: history.location,
});
React.useLayoutEffect(() => history.listen(setState), [history]);
return (
<Router
basename={basename}
children={children}
location={state.location}
navigationType={state.action}
navigator={history}
/>
);
}
export {
browserHistory as history,
BrowserRouter
}
實(shí)現(xiàn)Prompt
import React, { useEffect } from 'react';
import {history} from '../BrowserRouter';
import {useLocation, useNavigate} from 'react-router-dom';
const Prompt = (props) => {
const location = useLocation();
const navigate = useNavigate();
// 存儲(chǔ)關(guān)閉阻止頁(yè)面切換的方法(調(diào)用此方法將關(guān)閉阻止頁(yè)面切換)
let unblock = null;
// 阻止頁(yè)面卸載
const beforeUnload = (event) => {
event.preventDefault();
event.returnValue = '';
}
// 頁(yè)面切換時(shí)的回調(diào)
const handlePageChange = async ({location,action}) => {
// 是否關(guān)閉切換限制并跳轉(zhuǎn)
let toNext = false;
if (props.message) {
if (typeof props.message === "string") {
toNext = confirm(props.message);
} else {
toNext = await props.message(location,action);
}
} else {
toNext = confirm("是否放棄更改");
}
toNext && closeBlockAndNavigate(nextLocation);
}
// 關(guān)閉阻止頁(yè)面切換
const closeBlockPageSwitching = () => {
if (unblock) {
unblock();
unblock = null;
window.removeEventListener("beforeunload", beforeUnload);
}
}
// 關(guān)閉阻止頁(yè)面切換,并跳轉(zhuǎn)
const closeBlockAndNavigate = (nextLocation) => {
closeBlockPageSwitching();
navigate(nextLocation);
}
// 監(jiān)聽(tīng)when 和 pathname 變化,當(dāng)發(fā)生變化時(shí)判斷是否需要開(kāi)啟block navigate.
useEffect(() => {
if (props.when) {
// 阻塞頁(yè)面跳轉(zhuǎn)(history行為)
unblock = history.block(handlePageChange);
window.addEventListener('beforeunload', beforeUnload);
}
return () => {
props.when && closeBlockPageSwitching();
}
}, [props.when, location.pathname]);
return (
<></>
);
}
export default Prompt;
6. history對(duì)象廢棄,路由跳轉(zhuǎn)使用navigate代替
// old
history.go(n)
history.goBack()
history.goForward()
history.push(location);
history.replace(location);
// new
navigate(-1); // history.goBack();
navigate(1); // history.goForward();
navigate(location); // history.push(location);
navigate(location, { replace: true }); // history.replace(location);
7. 路由傳參方式修改及參數(shù)獲取方式
// old
history.push({
pathname: '/home',
search: qs.stringify({ the: 'query' }),
state: { some: 'state' },
query: 'state'
});
history.push( '/home',{ some: 'state' })
//取值
const {pathname, hash, state, search, query} = history.location;
// new
navigate( '/home', {state: {some: 'state' },replace: true,})
navigate({ pathname: '/home', search: qs.stringify({ the: 'query' }) })}
const { state, pathname, search} = this.props.location;
8. 刪除match對(duì)象
// old
const {params, url, path, isExact} = this.props.match;
// new
const { params } = this.props;
const { state, pathname, search} = this.props.location;