【问题标题】:React router: How to update component outside route on param change?反应路由器:如何在参数更改时更新路由外部的组件?
【发布时间】:2018-06-18 00:39:16
【问题描述】:

当 url 参数发生变化时,我需要更新两个组件,但其中一个组件在带有参数的路由之外。 App.js 中的路由是这样的:

<BrowserRouter>
  <div>
       <Route exact path="/" render={ (props) =>
         <Home products={this.state.products} } 
       />
        <Route path="/products/:product" render={ (props) => 
          <Product {...props} /> } 
        /> 

        <Route path="/"  render={ props => 
            <ProductHistory {...props}/>  }
         /> 
  </div>   

</BrowserRouter> 

始终可见的 ProductHistory 具有指向产品的链接,例如:

<Link to={`/products/${product.product_id}`}> {product.name}</Link>

当关注这样的链接时,使用 ComponentWillReceiveProps 方法更新 Product 组件:

componentWillReceiveProps(nextProps) {
    if(nextProps.match.params.product !== this.props.match.params.product){

但是如何在产品参数更改的同时更新 ProductHistory 组件?由于它不在 /products/:product 路由内,因此在 ProductHistory 的 componentWillReceiveProps 中检查 this.props.match.params.product 会得到 undefined。 (edit - withRouter 也无济于事,因为它已经在一个路由中,但是另一个路由:“/”)

在 componentWillReceiveProps 中我可以使用location.pathname 来检查路径是否以“/product”开头,我可以通过substr(path.lastIndexOf('/') + 1 找到参数。

编辑:但我还必须将当前 id 参数与下一个产品 id 参数进行比较,以避免不必要的更新。但是当点击链接时,当 componentWillReceiveProps 触发时,url 已经改变,所以location.pathnamenextProps.location.pathname 总是匹配,所以它会不必要地更新(重复的 api 调用)。

所以我必须找到不同的解决方案 - 以某种方式重新安排路由?这个想法是 ProductHistory 应该始终可见。

【问题讨论】:

  • 为什么不把ProductHistory组件放在Product下面呢?为什么你需要为他们单独的路线?
  • 但是它只会显示在产品页面上,所以我必须复制它并将其放在其他路线上,我认为这会导致问题。
  • 如果你的ProductHistory 依赖于:product_id,那么它就属于那里。如果用户导航到/blahProductHistory 的内容是什么?
  • 产品历史记录显示访问过的产品(存储在会话中),因此每次访问产品时,都需要使用该产品更新历史记录。任何其他页面只会显示存储的产品历史记录。
  • 将状态列表到包含ProductProductHistory 组件的父组件中。在父级上使用状态并将父级的setState 回调传递给Product 页面,并在componentDidMount 上使用正确的产品数据调用它。将父级的状态传递给ProductHistory 以呈现列表。在父组件的 render 方法中使用来自 react router 的Switch

标签: reactjs react-router-v4


【解决方案1】:

对于遇到此问题的任何人,钩子提供了一种新的解决方案,其形式为 useRouteMatch in react-router-dom

您可以像 João Cunha 的示例一样布置您的代码,其中 ProductHistory 不包含在 Route 中。如果ProductHistory 在产品的Route 之外的任何其他位置,所有正常的路由信息​​似乎都会给出错误的答案(我认为这可能是withRouter 的问题出现在对João 解决方案的回复中) ,但那是因为它不知道产品路由路径规范。与大多数 MVC 路由器略有不同,React-router-dom 不会计算预先匹配的路由,它会使用每个 Route 路径规范测试您所在的路径,并为组件生成特定的路由匹配信息在那个Route下。

所以,这样想:在ProductHistory 组件中,您使用useRouteMatch 来测试路由是否匹配您可以从中提取所需参数的路径规范。例如

import { useRouteMatch } from 'react-router-dom';
const ProductHistory = () => {
  const { params: { product } } = useRouteMatch("/products/:product");
  return <ProductList currentProduct={product || null} />;
};

这将允许您针对可能适用于产品的多个 URL 进行测试和匹配,这是一个非常灵活的解决方案!

【讨论】:

    【解决方案2】:

    你可以像这样简单地渲染路由:

    <BrowserRouter>
      <div>
      <Switch>
        <Route exact path="/" render={ (props) =>
          <Home products={this.state.products} } 
        />
        <Route path="/products/:product" render={ (props) => 
          <Product {...props} /> } 
        /> 
      </Switch>
      <ProductHistory />
      </div>
    </BrowserRouter>
    

    然后在 ProductHistory 类中使用withRouter HOC

    您可以访问历史对象的属性和最近的 Route 通过 withRouter 高阶组件匹配。 带路由器 将更新的匹配、位置和历史道具传递给包装的 组件无论何时呈现。

    示例:

    class ProductHistory extends Component { ... }
    
    export default withRouter(ProductHistory);
    

    或使用装饰器

    @withRouter
    export default class ProductHistory extends Component { ... }
    

    有了这个,你将能够通过这样的道具访问比赛、位置和历史:

    this.props.match
    this.props.location
    this.props.history
    

    【讨论】:

    • 当我添加 withRouter ,并在 componentWillReceiveProps 中提醒 JSON.stringify(this.props.match) 时,数据为空:{"path":"/","url":"/", "isExact":false,"params":{}}
    • @Galivan 这并不意味着它是空的。通常,如果您不使用 withRouter,那只会给您未定义。那将是空数据。这意味着你在路线“/”
    • 不,如果执行 JSON.stringify(this.props.match),即使没有“withRouter”,我也会得到相同的结果 - 因为 productHistory 已经拥有路由器(在
    • @Galivan 您需要从 react-router-dom 切换以进行一些路由切换,然后您始终可以根据需要将 ProductHistory 呈现在外部,并且由于组件位于 BrowserRouter 内部,因此它必须工作。我有一堆项目在做这样的事情,他们都工作。
    猜你喜欢
    • 2018-01-05
    • 2018-11-13
    • 1970-01-01
    • 2017-02-23
    • 2021-12-23
    • 2022-06-10
    • 1970-01-01
    • 2017-08-25
    • 2020-05-21
    相关资源
    最近更新 更多