像素

不同情况下页面返回上一页处理

页面的返回一般处理成两种方式,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 });
  }
};