react-router 如何在不同页面之间缓存数据.

220 阅读1分钟

使用react-router时, 如何在不同页面之间缓存数据.

<Routes>
  <Route path="/" element={<Layout />}>
    <Route index element={<Home />} />
    <Route path="about" element={<About />} />
    <Route path="dashboard" element={<Dashboard />} />
    <Route path="*" element={<NoMatch />} />
  </Route>
</Routes>
// Route 是短路操作
function Route() {
  // ...
  return isMatch ? element : null;
}

当页面切换时组件会卸载.

这个行为是很合理的.

再想想我们的需求,我们想缓存什么?

  1. 数据请求

一般我们会在当前页面渲染时获取数据

useEffect(() => {
  getDate();
}, []);

手动控制请求缓存时很困难的.

现在我们使用react query设置 1 分钟缓存时间, 缓存期间重新进入就不会在获取数据.

import { useQuery } from "react-query";
export function useQueryData() {
  return useQuery(
    "cachedData",
    () => {
      return new Promise((resolve) => {
        console.log("request");
        setTimeout(() => {
          return resolve({
            username: "aaa",
          });
        }, 1000);
      });
    },
    {
      refetchOnWindowFocus: false,
      staleTime: 60 * 1000,
    }
  );
}

function Home() {
  const { data: user } = useQueryData();
  return (
    <div>
      <h2>{user?.username}</h2>
    </div>
  );
}
  1. 状态

我们可以通过状态提升来保存一些状态

// before
function About() {
  const [show, setShow] = useState(false);
  return (
    <button onClick={() => setShow((show) => !show)}>
      {show ? "show" : "hidden"}
    </button>
  );
}
// after
function Layout() {
  const [show, setShow] = useState(false);
  const value = useMemo(() => {
    return {
      show,
      setShow,
    };
  });
  return (
    <div>
      ...
      <AboutContext.Provider value={value}>
        <Outlet />
      </AboutContext.Provider>
    </div>
  );
}
function About() {
  const { show, setShow } = useAbout();
  return (
    <button onClick={() => setShow((show) => !show)}>
      {show ? "show" : "hidden"}
    </button>
  );
}
  1. 缓存整个页面

优先考虑状态提升

如果必须要缓存某个页面, 我的想法是不用路由,仅控制可见性.

function Layout() {
  return (
    <div>
      ...
      <Dashboard />
      <Outlet />
    </div>
  );
}

function Dashboard() {
  const { pathname } = useLocation();
  const isMatch = pathname === "/dashboard";

  useEffect(() => {
    console.log("mount");
    return () => {
      console.log("unmount");
    };
  }, []);

  return (
    <div style={{ display: !isMatch ? "none" : undefined }}>
      <h2>Dashboard</h2>
    </div>
  );
}

完整代码: codesandbox.io/p/sandbox/c…