You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Router v6中如何向Link组件传递函数

问题根源

React Router 的 Link 组件的 state 参数底层依赖浏览器 History API,传入的值会经过结构化克隆算法处理,函数、Symbol、含循环引用的对象、类实例这类不可序列化的值无法被克隆,所以传递后在目标页通过 useLocation 取值会得到 null;可序列化的普通类型(字符串、数字、普通对象、数组等)可以正常传递,是因为符合结构化克隆的要求。

正确实现方案

你尝试的 React Context 思路本身是可行的,之前代码不生效的核心原因是:RefreshDisplayContext.Provider 被写在了 Launchpad 组件内部,而 /login 路由对应的 Login 组件和 Launchpad 是路由平级关系,不在 Context Provider 的子树范围内,自然拿不到 Context 值。
调整方式如下:

方案1:修正Context作用域(推荐)

把 Context Provider 提升到路由层外层,让所有需要访问该方法的路由组件都处于 Provider 的子树中:

  1. 把 Context 定义、关联的共享状态统一提升到根入口组件,或抽到单独的公共Context文件:
// 入口文件 main.jsx
import ReactDOM from "react-dom/client";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import React, { useState, useCallback } from "react";
import { Launchpad } from "./routes/Launchpad";
import { Login } from "./routes/Login";

// 导出Context供子组件引入使用
export const RefreshDisplayContext = React.createContext(() => {});

function App() {
  const [launchpadState, updatelaunchpadState] = useState({
    userIsLoggedIn: false
  });

  // 用useCallback包裹避免每次渲染函数引用变化
  const refreshDisplay = useCallback((newState) => {
    updatelaunchpadState(prev => ({ ...prev, ...newState }));
  }, []);

  return (
    <BrowserRouter>
      {/* Provider包裹整个路由区域,所有路由组件都能访问到Context值 */}
      <RefreshDisplayContext.Provider value={{ refreshDisplay, launchpadState }}>
        <Routes>
          <Route path="/" element={<Launchpad />} />
          <Route path="/login" element={<Login />} />
        </Routes>
      </RefreshDisplayContext.Provider>
    </BrowserRouter>
  )
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
  1. Launchpad 组件修改为直接消费Context里的状态和方法,移除内部多余的Provider和重复状态:
// routes/Launchpad.jsx
import React, { useContext } from "react";
import { Link } from "react-router-dom";
import { RefreshDisplayContext } from "../main";

function Launchpad() {
  const { launchpadState } = useContext(RefreshDisplayContext);

  return (
    <div>
      {launchpadState.userIsLoggedIn ? (
        <p>User is Logged In </p>
      ) : (
        <p>User is Not Logged In </p>
      )}
      <Link to="login">
        Login via Child
      </Link>
    </div>
  );
}

export { Launchpad };
  1. 原有Login组件不需要修改,就可以正常调用Context里的refreshDisplay方法更新状态。

方案2:传可序列化标识,目标页映射对应方法

如果不想用Context,可以遵循路由state的序列化设计要求,不要直接传函数,只传可序列化的字符串/数字标识,在目标路由页面提前引入所有可能用到的函数,根据传过来的标识匹配对应方法执行即可。

// 传参页
<Link to="/LoginScreen" state={{ funcType: 'refreshLogin' }} style={style.a} >
  Login
</Link>

// 目标页 LoginScreen
import { useLocation } from "react-router-dom";
// 提前引入需要调用的方法
import { refreshLogin, refreshUserInfo } from "../utils/actions";

const funcMap = {
  refreshLogin,
  refreshUserInfo
}

function LoginScreen() {
  const location = useLocation();
  const { funcType } = location.state || {};
  const targetFunc = funcMap[funcType];

  // 直接调用targetFunc即可
  return <button onClick={targetFunc}>登录</button>
}

方案3:使用全局状态管理

如果项目中已经接入了Redux、Zustand、Jotai这类全局状态库,可以直接把需要跨路由调用的方法存在全局Store中,跳转后直接从Store中取方法调用即可,逻辑和Context方案一致,适合更复杂的跨组件/跨路由状态共享场景。

注意:不要尝试通过JSON序列化/反序列化的方式传递函数,这类方式会丢失函数的闭包作用域,执行时会出现意料之外的报错,完全不符合React和路由的设计规范。

内容的提问来源于stack exchange,提问作者Utonyum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 03:15:47