强制 React-Router <Link> 加载页面,即使我们已经在该页面上

Posted

技术标签:

【中文标题】强制 React-Router <Link> 加载页面,即使我们已经在该页面上【英文标题】:Forcing a React-Router <Link> to load a page, even if we're already on that page 【发布时间】:2016-12-14 20:05:09 【问题描述】:

有没有办法强制 React-Router &lt;Link&gt; 从路径加载页面,即使当前位置已经是该页面?我似乎在 react-router 文档中找不到任何提及。

我们在“申请”的路线上有一个页面,它会加载带有主图、一些说明性文字等的登录页面,以及一个“申请这个程序”按钮,它可以交换作为应用程序的内容形式。这一切都发生在同一个“申请”路线上,因为用户不应该在没有首先点击登录页面的情况下直接导航到这个表单。

但是,当他们打开此表单并再次单击导航菜单中的应用链接时,整个页面应该重新加载,就像在第一次安装时一样,让他们“返回”(但实际上是转发)到再次登陆页面。

相反,单击&lt;Link&gt; 什么都不做,因为 react-router 看到我们已经在“应用”页面上,因此不会卸载当前页面然后安装另一个页面。

有没有办法在安装请求的页面之前强制卸载当前页面,即使它是针对用户应该已经在的页面? (例如通过&lt;Link&gt; 属性?)

【问题讨论】:

【参考方案1】:

不是一个好的解决方案,因为它会强制刷新整个页面并引发错误,但您可以使用 onClick 处理程序调用 forceUpdate(),例如:

<Link onClick=this.forceUpdate to='/the-page'>
    Click Me
</Link>

我只能说它有效。我自己也遇到了类似的问题,希望其他人有更好的答案!

React router Link not causing component to update within nested routes

【讨论】:

【参考方案2】:

我用来解决我的小需求的一个解决方法是更改​​ React-Router 所查看的location。如果它看到我们已经在的位置(如您的示例中),它不会做任何事情,但是通过使用位置对象并更改它,而不是使用纯字符串路径,React-Router 将“导航”到新位置,即使路径看起来相同。

您可以通过设置一个与当前键不同的key 来做到这一点(类似于 React 的渲染依赖于key)和一个state 属性,允许您围绕您想要做的事情编写清晰的代码:

render() 
  const linkTarget = 
    pathname: "/page",
    key: uuid(), // we could use Math.random, but that's not guaranteed unique.
    state: 
      applied: true
    
  ;

  return (
    ...
    <Link to=linkTarget>Page</Link>
    ...
  );

请注意(令人困惑)您告诉Link 您需要将哪些值作为state 对象传递,但链接会将这些值作为props 传递到组件中。所以不要误以为在目标组件中尝试访问this.state

然后我们可以在目标组件的componentDidUpdate 中检查这一点,如下所示:

componentDidUpdate(prevProps, prevState, snapshot) 
  // Check to see if the "applied" flag got changed (NOT just "set")
  if (this.props.location.state.applied && !prevProps.location.state.applied) 
    // Do stuff here 
  

【讨论】:

请注意,从 React 16 开始,componentWillReceiveProps 生命周期函数已被弃用,并将在 React 17 中删除。我已经更新了您的答案,但也添加了一些实际解释为什么它有效的位,加上key,因为这是可靠地使事情正常工作的部分。只需添加一个state 确实会产生一个新的location,但现在我们不能再“重新加载”相同的路径+状态而不更改其他内容。所以正是出于这个原因,“其他东西”是位置的key 属性=) @Mike'Pomax'Kamermans 在这种情况下,uuid 是什么? 任何有效的 uuid,如全局 BigInteger、npmjs.com/package/uuid 等。【参考方案3】:

你可以使用生命周期方法——componentWillReceiveProps 当您单击链接时,位置道具的键会更新。所以,你可以做一个解决方法,如下所示,

/**
 * @param object nextProps new properties
 */
    componentWillReceiveProps = (nextProps)=> 
        if (nextProps.location.pathname !== this.props.location.pathname) 
            window.location.reload();
        
    ;

【讨论】:

当 2015 年接受的答案已经说明了这一点时,你为什么要发布这个。如果您看到一个已接受答案的问题,请不要发布相同的答案,找一个新问题来回答。【参考方案4】:

在 Route 组件中,指定一个随机键。

<Route path=YOURPATH render=(props) => <YourComp ...props keyProp=someValue key=randomGen()/> />

当 react 看到不同的 key 时,会触发重新渲染。

【讨论】:

虽然我喜欢这背后的想法,但您的答案远未完成,仍然需要解释如何以实际触发重新渲染的方式触发对该键的更改。 我发现这实际上是个好主意!易于实施且完美运行 我使用Math.random 生成密钥。基本上发生的事情是,当您单击链接时,react router 会评估您的箭头函数以获取组件,并通过每次使用 random 传递一个新键来确保每次链接单击都被视为 prop 更改,react 将重新呈现。跨度> 您的解决方案给了我不变的违规:超过了最大更新深度。我认为对于复杂的组件来说这不是一个好主意 YourComp 将在重新渲染后失去它的状态(这并不总是由导航触发)。它可能会产生不必要的副作用,例如丢失滚动位置。最好仅在发生导航时更新密钥。【参考方案5】:

我通过将新路线推送到历史记录中解决了这个问题,然后用当前路线(或您要刷新的路线)替换该路线。这将触发 react-router 在不刷新整个页面的情况下“重新加载”路由。

<Link onClick=this.reloadRoute() to='/route-to-refresh'>
    Click Me
</Link>

let reloadRoute = () => 
    router.push( pathname: '/empty' );
    router.replace( pathname: '/route-to-refresh' );

React 路由器的工作原理是使用您的浏览器历史记录进行导航,而无需重新加载整个页面。如果你强制一个路由进入历史,react 路由器会检测到这个并重新加载路由。替换空路线很重要,这样您的后退按钮不会在您按下后将您带到空路线。

根据react-router 看来,react 路由器库不支持此功能,而且可能永远不会支持,因此您必须以一种骇人听闻的方式强制刷新。

【讨论】:

【参考方案6】:

尝试仅使用锚标记作为 href 链接。在标签中使用target="_self" 强制页面完全重新呈现。

【讨论】:

这特别是关于在使用并依赖、组件进行导航、服务器端生成等的代码库的上下文中要做什么。“只是使用其他东西”不能成为任何答案的一部分。【参考方案7】:

这可能是一个常见问题,我正在寻找一个合适的解决方案,以便下次在我的工具包中使用。 React-Router 提供了一些机制来了解用户何时尝试访问任何页面,甚至是他们已经访问的页面。

阅读location.key 哈希,这是一种完美的方法,因为每次用户尝试在任何页面之间导航时它都会改变。

componentDidUpdate (prevProps) 
    if (prevProps.location.key !== this.props.location.key) 
        this.setState(
            isFormSubmitted: false,
        )
    

设置新状态后,调用渲染方法。在示例中,我将状态设置为默认值。

参考:A location object is never mutated so you can use it in the lifecycle hooks to determine when navigation happens

【讨论】:

这里的一个缺点当然是这需要componentDidUpdate,在每个道具和状态更新上不断运行代码,即使这几乎永远不会成为更改此代码测试。一个更有效的解决方案会很好。【参考方案8】:

使用 Rachita Bansal 答案解决,但使用 componentDidUpdate 代替 componentWillReceiveProps

componentDidUpdate(prevProps) 
    if (prevProps.location.pathname !== this.props.location.pathname)  window.location.reload();
    

【讨论】:

你可能想评论他们的答案,以便他们可以更新它,因为“willupdate”功能在很多 React 版本之前已经被弃用了。【参考方案9】:

说实话,这些都不是真正的“思考 React”。对于那些遇到这个问题的人,完成相同任务的更好选择是使用组件状态。

将路由组件的状态设置为布尔值或您可以跟踪的值:

this.state = 
    isLandingPage: true // or some other tracking value
;

当你想去下一条路线时,只需更新状态并将你的渲染方法加载到所需的组件中。

【讨论】:

这听起来很像您没有阅读问题,因为这非常并没有解决那里列出的问题。 @Mike'Pomax'Kamermans 我确实读过这个问题。我来到这里是因为我最初有同样的问题。问题是,正如许多人之前回答的那样,react-router 不是以这种方式运行的。这反过来又让你用其他建议来回答这个问题,而这里的大多数都是黑客。秉承本论坛的真正精神,我提供了另一种替代方案,该替代方案与“思考 React”的口号保持一致,以便其他遇到相同问题的人可以看到非 hack 解决方案。 除此之外,解决方案是记住在您的路线中使用key,按照我接受的答案,其中包含解释该问题的文档的链接。正确使用key 属性实际上是使用 React 的关键部分。如果只是“更新状态值”有效,我就没有任何理由发布这个问题。显然状态值已经更新,问题是关于创建硬导航点(您的后退/前进按钮使用的那些),状态更新不会影响。【参考方案10】:

我的工作方式与@peiti-li 在 react-router-dom v5.1.2 中的回答略有不同,因为在我的情况下,我的页面在尝试他们的解决方案后陷入了无限渲染循环。

以下是我所做的。

<Route
  path="/mypath"
  render=(props) => <MyComponent key=props.location.key />
/>

每次发生路由更改时,location.key 属性都会更改,即使用户已经在同一条路由上。根据 react-router-dom 文档:

而不是使用为您创建新的 React 元素 组件属性,你可以传入一个函数来调用,当 位置匹配。 render prop 函数可以访问所有相同的 路由道具(匹配,位置和历史)作为组件渲染 道具。

这意味着当路由发生变化时,我们可以使用props.location.key 来获取更改键。将此传递给组件将使组件在每次键更改时重新渲染。

【讨论】:

【参考方案11】:

简单如下:

<Route path="/my/path" render=(props) => <MyComp ...props key=Date.now()/> />

对我来说很好用。定位到同一路径时:

this.props.history.push("/my/path");

即使我已经在/my/path,页面也会重新加载。

【讨论】:

是的,因为那个key,就像已经说过的接受和回答一样。【参考方案12】:

这是一个不需要更新任何下游组件或更新大量路由的 hacky 解决方案。我真的不喜欢它,因为我觉得 react-router 中应该有一些东西可以为我处理这个问题。

基本上,如果链接是针对当前页面的,那么点击...

    等到当前执行之后。 将历史记录替换为/refresh?url=&lt;your url to refresh&gt;。 让您的交换机监听/refresh 路由,然后让它重定向回url 查询参数中指定的网址。

代码

首先在我的链接组件中:

function MenuLink( to, children ) 
    const location = useLocation();
    const history = useHistory();
    const isCurrentPage = () => location.pathname === to;
    const handler = isCurrentPage() ? () => 
        setTimeout(() => 
            if (isCurrentPage()) 
                history.replace("/refresh?url=" + encodeURIComponent(to))
            
        , 0);
     : undefined;

    return <Link to=to onClick=handler>children</Link>;

然后在我的开关中:

<Switch>
        <Route path="/refresh" render=() => <Redirect to=parseQueryString().url ?? "/" /> />
        /* ...rest of routes go here... */
<Switch>

...parseQueryString() 是我编写的用于获取查询参数的函数。

【讨论】:

鉴于存在,并且接受的答案涵盖了它......为什么这个答案? @Mike'Pomax'Kamermans 接受的答案似乎需要更新下游组件以检查状态以便知道刷新,而这个答案不需要。也只是在链接的“to”对象中提供一个随机键对我不起作用。也许我做错了什么? 另外,我使用的是 react-router / react-router-dom 5.2.0。也许只使用随机密钥而不更新状态在新版本中不起作用?或者那从来没有奏效?我刚试过Math.random().toString() 啊,是的,如果下游组件不可访问,事情会变得相当困难,但值得在这个答案前面加上那个特定的“为什么”。现在还不清楚为什么有人可能需要这个解决方案。已经好几年了,我已经完全离开了 React Router(因为我制作网站而不是 webapps,所以 react-router 不再提供任何有用的东西。只需在 CI/CD 和 dev-watch 期间预生成您的页面,并且您会变得无限好,并且尊重浏览器扩展,html/CSS/JS)【参考方案13】:

我遇到了同样的问题,我通过使用 href 而不是 Link 解决了

<a href='/'>

【讨论】:

这不是&lt;Link&gt; - 这个问题不是关于如何加载页面,而是非常具体地关于如何强制 &lt;Link> 生效重新加载它已经在的路线。【参考方案14】:

我找到了一个简单的解决方案。

<BrowserRouter forceRefresh />

这会在点击任何链接时强制刷新。不幸的是,它是全局的,所以你不能指定只刷新哪些链接/页面。

来自documentation:

如果为 true,路由器将在页面导航上使用全页面刷新。您可能希望使用它来模仿传统的服务器渲染应用在页面导航之间进行全页面刷新的方式。

【讨论】:

你真的需要在这里添加 很多 更多细节,因为这是 2016 年的一个问题,对于 React 库来说这是一个很小的永恒。如果您的 2021 年答案依赖于 年后 引入的元素,请说明这适用于哪个版本的 React Router,以及文档适用于它的位置 =) 我不知道这个问题的由来,但这个答案肯定适用于我。

以上是关于强制 React-Router <Link> 加载页面,即使我们已经在该页面上的主要内容,如果未能解决你的问题,请参考以下文章

React-router <Link> 没有活动类

React-router:使用 <Link> 作为可点击的数据表行

[react-router] React-Router的<Link>标签和<a>标签有什么区别

在将 react-router 5 与 redux 7 一起使用时,react-router <Link> 在转到新路由后不会重置状态

通过 React-Router v4 <Link /> 传递值

ReactJS + React Router:如何从 React-Router 的 <Link> 禁用链接?