React Router DOM v5:复用Link的to参数至onClick并封装组件
问题分析
你之前的自定义组件未生效的核心原因是:to参数传入的是函数(如addVolumeLink),你直接将函数本身传给了postMessage,而没有解析出实际的路由路径对象,导致父iframe无法拿到有效路由信息。同时,组件没有兼容原有自定义onClick的场景,也可能存在覆盖问题。
可行方案
下面提供两种通用实现方式,只需替换原有Link/NavLink的import即可全局复用逻辑,无需修改上百处代码。
方式1:封装独立的自定义Link/NavLink组件
自定义Link组件
import { Link, useLocation } from "react-router-dom"; // 向父iframe发送导航事件 const navigateToParentIframe = (payload) => { window.parent.postMessage( { type: 'SERVICE:NAVIGATE', payload, }, "*" // 建议替换为父页面的具体域名,提升安全性 ); }; export const StyledLink = ({ children, to, onClick: propsOnClick, ...rest }) => { const location = useLocation(); // 解析to参数:如果是函数,传入当前location得到实际路由对象 const resolvedTo = typeof to === 'function' ? to(location) : to; const handleClick = (e) => { // 优先执行组件原有onClick逻辑(如果存在) if (propsOnClick) { propsOnClick(e); } // 发送路由信息给父iframe navigateToParentIframe(resolvedTo); }; return ( <Link to={to} onClick={handleClick} children={children} {...rest} /> ); };
自定义NavLink组件(逻辑一致)
import { NavLink, useLocation } from "react-router-dom"; const navigateToParentIframe = (payload) => { window.parent.postMessage( { type: 'SERVICE:NAVIGATE', payload, }, "*" ); }; export const StyledNavLink = ({ children, to, onClick: propsOnClick, ...rest }) => { const location = useLocation(); const resolvedTo = typeof to === 'function' ? to(location) : to; const handleClick = (e) => { if (propsOnClick) { propsOnClick(e); } navigateToParentIframe(resolvedTo); }; return ( <NavLink to={to} onClick={handleClick} children={children} {...rest} /> ); };
方式2:用高阶组件(HOC)统一包装Link/NavLink
如果需要更灵活的复用,可以用HOC同时包装Link和NavLink:
import { useLocation } from "react-router-dom"; const navigateToParentIframe = (payload) => { window.parent.postMessage( { type: 'SERVICE:NAVIGATE', payload, }, "*" ); }; // 通用高阶组件,接收Link/NavLink组件并增强其功能 const withIframeNavigation = (Component) => { return ({ to, onClick: propsOnClick, ...rest }) => { const location = useLocation(); const resolvedTo = typeof to === 'function' ? to(location) : to; const handleClick = (e) => { if (propsOnClick) { propsOnClick(e); } navigateToParentIframe(resolvedTo); }; return <Component to={to} onClick={handleClick} {...rest} />; }; }; // 导出增强后的组件 import { Link, NavLink } from "react-router-dom"; export const StyledLink = withIframeNavigation(Link); export const StyledNavLink = withIframeNavigation(NavLink);
使用方式
直接替换原有react-router-dom的import即可,原有代码无需其他修改:
// 替换前 // import { Link } from "react-router-dom"; // 替换后 import { StyledLink } from "./myLink"; const addVolumeLink = location => ({ pathname: location.pathname + "/add-volume", state: { from: location.pathname } }); const myMainComponent = () => { return (<StyledLink to={addVolumeLink}> New volume </StyledLink>); }
注意事项
- 确保自定义组件在
<Router>上下文内:因为用到了useLocation钩子,组件必须被包裹在react-router-dom的<Router>组件中(绝大多数React Router项目都满足此条件)。 - 安全优化:将
postMessage的目标域名从*改为父页面的具体域名,避免潜在的安全风险。 - 兼容原有逻辑:组件会优先执行原有
onClick回调,再发送iframe事件,不会覆盖业务自定义逻辑。
内容的提问来源于stack exchange,提问作者Pradip
相关产品推荐
相关产品推荐

