不同情况下页面返回上一页处理
页面的返回一般处理成两种方式,goback 返回上一个 history,push 进一个新的 history。 有这样的页面 home -> pageA -> pageB.
第一种方式,正常情况下用户从 home 访问 pageA 再进到pageb,此时页面返回 goback 是没有问题的。 但是如果用户通过链接的方式直接访问 pageA 这时候点 pageA 的返回。因为 history 中没有历史记录会出现问题。
第二种方式,如果 有一个 pageC 也可以进入 pageA 的话。因为 pageA 的返回是 push('/home')。 所以会直接返回 home,不会返回 pageC,会造成用户困扰。
如果能知道当前页面的前一个 history 的信息的话,可以依据来进行判断
- 如果前一个 history 存在的情况下,goback 返回
- 如果前一个 history 不存在的话,push 返回
react 中实现方案 通过 ref 结合 react-router 中的 useLocation 来进行记录页面的 location 信息。 **利用 ref 在 layout 时更新的特点**,将 ref 的赋值放在 useeffect 中进行。 effect 回调的执行在每次的 render 中,commit 阶段 layout 之前。 这样就使得每次 render 中 ref 只会记录上一次的 location 信息,依此来进行判断。
import React, { useContext, useEffect, useRef } from "react";
import { useLocation, Location } from "react-router-dom";
// 将每次render中记录的上一个location 存放在context
const LocationContext = React.createContext<Location | undefined>(undefined);
interface LastLocationProviderProps {
lastLocation?: Location;
children?: React.ReactNode;
}
/**
* 暴露出获取location的方法,在路由页面使用
*/
export const useLastHistory = () => {
return useContext(LocationContext);
};
/**
*
* @param children
* @constructor
*/
const LastLocationProvider: React.FC<LastLocationProviderProps> = ({
children,
}) => {
const lastHistory = useRef<Location>();
const location = useLocation();
// 通过ref 在layout 时才更新的特点,每次记录上一个路由的location
useEffect(() => {
lastHistory.current = location;
}, [location]);
return (
<LocationContext.Provider value={lastHistory.current}>
{children}
</LocationContext.Provider>
);
};
export default LastLocationProvider;// 包裹在路由外层
function App() {
return (
<BrowserRouter>
<LastLocationProvider>
<Suspense fallback={<Loading />}>
<Routes>
<Route path={"/"} element={<Home />} />
<Route path={"/PageA"} element={<PageA />} />
<Route path={"/PageB"} element={<PageB />} />
<Route path={"/PageC"} element={<PageC />} />
<Route path={"*"} element={<Error />} />
</Routes>
</Suspense>
</LastLocationProvider>
</BrowserRouter>
);
}在路由页面利用 useLastHistory 进行判断前一个路由存在 goback,不存在 push 进 home
import { useNavigate } from "react-router-dom";
import { useLastHistory } from "../../../components/lastLocationProvider";
const PageA = () => {
const lastHistory = useLastHistory();
const navigate = useNavigate();
const goBack = () => {
if (lastHistory) {
navigate(-1);
} else {
// 前一个路由不存在 直接返回home
navigate("/home");
}
};
//... 略
};但是这样还不够,还是以 home -> pageA -> pageB. 为例
用户直接通过链接访问 pageB,此时 pageB 会因为前一个路由不存在,返回 pageA。 这时候 pageA 会因为前一个路由存在走 goback,这样就又返回到了 pageB。
完善一下,不采用 push,因为每次 push 都会在 history 中新增一条记录,从而影响到,返回的判断。 换成 replace,但是前面记录上一个 history 的实现中 ref 会在 layout 阶段更新,所以要更改一下判断
const goBack = () => {
const canBack =
lastHistory && lastHistory.key !== "default" && navtype !== "REPLACE";
if (canBack) {
navigate(-1);
} else {
navigate("/", { replace: true });
}
};