后退按钮后反应路由器“历史”和“位置”不匹配

Posted

技术标签:

【中文标题】后退按钮后反应路由器“历史”和“位置”不匹配【英文标题】:React Router `history` and `location` mismatched after back button 【发布时间】:2020-04-22 11:07:56 【问题描述】:

在我的 React 应用程序中,从主页导航到子页面然后点击后退按钮后,React <Router> 显示其内部状态与从 history 传递的道具不匹配的位置以及不正确的 <Route>为地址栏中当前显示的 URL 显示

<Route> 的内容确实发生了变化;数据由位置更改 Redux 操作更新。显示的<Route>显示的是新数据,但是是错误的<Route>

应用的基本结构是:

<Root>                                  (custom)
  <Provider>                            (react-redux)
    <ConnectedRouter>                   (connected-react-router)
      <App>                             (custom)
        <Switch>                        (react-router-dom)
          <Route path="/" exact />      (react-router-dom)
          <Route path="/quality:q" />   (react-router-dom)

最初我们导航到根目录,&lt;Route path="/&gt; 正确显示。我们单击一个链接,该链接正确地将我们带到&lt;Route path="/quality/:q" /&gt;。从那里我们返回浏览器,URL 更改为 /,但 &lt;Route path="/quality:q" /&gt; 仍然显示,但它不应该再显示了。

Redux devtools 显示了这个渲染树,我在其中包含了一些由我们明确使用的组件添加的关键组件。

<Root>                                  (custom)
  <Provider>                            (react-redux)
    <ConnectedRouter>                   (connected-react-router)
      <Router>                          (injected)
        <Context.Provider>              (injected)
          <App>                             (custom)
            <Switch>                        (react-router-dom)
              <Route path="/" exact />      (react-router-dom)
              <Route path="/quality:q" />   (react-router-dom)

当处于这种错误状态时,直到&lt;ConnectedRouter&gt; 的所有内容都是正确的。 &lt;ConnectedRouter&gt; 有一个单独的 prop,它是 history,它具有正确的位置,即根。

紧随其后的&lt;Router&gt; 不匹配。它有一个 history 属性,没问题,但它有一个陈旧的 location 状态,显示旧网址。

当错误的位置数据被上下文传递给&lt;Route&gt;s 时,他们会从location 属性中获得检查路径,该属性匹配&lt;Router&gt; 的陈旧状态。结果,渲染了错误的&lt;Route&gt;

我无法弄清楚我做了什么导致这种不匹配。这都是简单的路线,并使用&lt;Link&gt; 组件作为从主页到子页面和返回的所有链接。

以前有没有人见过这个和/或可以指出如何解决这个问题的方向?

【问题讨论】:

我遇到了类似的问题,history.location 正确但 location 不正确,您能找出原因吗? 【参考方案1】:

我认为您的路由结构需要重构,因为您尝试匹配(可能是嵌套的)URL。

如果您希望/quality/:id 低于/,则Route 需要是component 的子代。

例如,Route 将是:

<Route path="/" component=Home />

然后在Home 组件中,您将拥有使用match.url (route props) 作为字符串匹配器的子Routes:

const Home = ( match:  url  ) => (
 <>
  <div>Home<Home>
  <Switch>
    <Route path=`$urlquality/:q` component=Quality />
    <Route path=`$urlsome/other/route` component=Example />
  </Switch>
 </>
);

但是,如果您想要相反的情况,即/ 独立于/quality/:p,那么您需要使用exact

<Switch>
   <Route exact path="/" component=Home />
   <Route exact path="/quality/:p" component=Quality />
   <Route exact path="/some/other/route" component=Example />
<Switch>

工作示例

在本例中,/home/about/dashboard 是相互独立的路由;但是,/dashboard 是这些子路由的父级:/dashboard/settings/dashboard(顺序很重要!)和 /dashboard 呈现一个 Dashboard 组件,它也恰好是 /dashboard/profile 子路由的父级! Order 和 exact 对于每个嵌套路由的渲染方式很重要。我还在pages/Profile 中包含了一个goBack 示例,以证明connected-react-router 的历史记录与react-router-dom 路由历史记录路由保持一致。

demo

source


如果您遵循上述约定,那么唯一的另一个问题是react-hot-loader 正在阻止状态更改。我个人不使用 RHL,因为我发现它相当有问题,但对每个人来说都是如此。也就是说,我在过去的 3 或 4 个项目中使用了connected-react-router,并且我从来没有遇到过 props/state 不匹配的情况(除了一些共享的 redux 数据是陈旧的并且需要在加载另一个组件之前重置)正在重用相同的数据结构)。

【讨论】:

感谢您的回复。在简化发布消息时,我在根路由上省略了 exact 关键字,但它在原始代码中。我更新了问题以澄清。 如果您可以创建可重现的代码和框演示(不需要花哨),那么它会更容易提供帮助。否则就是猜谜游戏。 同意,如果我可以重新创建,我可能会找到它。我正在逐行还原并重新应用自上次已知工作版本以来的更改,以查看导致此问题的原因。 要考虑的另一件事是,如果您使用的是服务工作者,那么它可能会缓存您的请求。 @SamuelNeff - 你最终解决了这个问题吗?如果是这样,解决方法是什么?【参考方案2】:

如果您使用 react-router 5+,您可以将位置从上层范围传递到 Switch。我从历史背景中传递过来。在我的情况下它解决了同样的问题

【讨论】:

【参考方案3】:

我遇到了类似的问题。事实证明,我的树上有一个PureComponent 导致了它。使用withRouter 并将location 传递给PureComponent 解决了这个问题。

import  withRouter  from "react-router-dom";

const App = withRouter(( location, children ) => (
  <IntlProvider location=location>
    children
  </IntlProvider>
));

ReactDOM.render(
  <Root>
    <Provider>
      <ConnectedRouter>
        <App>
          <Switch>
            <Route path="/" exact />
            <Route path="/quality:q" />
          </Switch>
        </App>
      </ConnectedRouter>
    </Provider>
  </Root>

【讨论】:

以上是关于后退按钮后反应路由器“历史”和“位置”不匹配的主要内容,如果未能解决你的问题,请参考以下文章

骨干路由器获取后退按钮的历史长度

在 goBack() 反应路由器 v4 之前检查历史以前的位置

按下后退按钮时防止反应路由器重新加载 API 调用。反应

Android后退按钮处理程序反应本机路由器通量

反应路由器 - 未定义的历史

哈希路由(hash模式)和历史路由(history模式)的区别