React 不会在路由参数更改或查询更改时重新加载组件数据

Posted

技术标签:

【中文标题】React 不会在路由参数更改或查询更改时重新加载组件数据【英文标题】:React doesn't reload component data on route param change or query change 【发布时间】:2018-06-16 18:45:41 【问题描述】:

我有一个带有链接的“主页”组件,当您单击链接时,产品组件会与产品一起加载。我还有另一个始终可见的组件,显示“最近访问的产品”的链接。

这些链接在产品页面上不起作用。当我单击链接时,url 会更新,并且会发生渲染,但产品组件不会随新产品更新。

看这个例子: Codesandbox example

这里是 index.js 中的路由:

<BrowserRouter>
  <div>
    <Route
      exact
      path="/"
      render=props => <Home products=this.state.products />
    />

    <Route path="/products/:product" render=props => <Product ...props /> />

    <Route path="/" render=() => <ProductHistory /> />

    <Link to="/">to Home</Link>
  </div>
</BrowserRouter>;

ProductHistory 中的链接如下所示:

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

所以它们匹配Route path="/products/:product"

当我在产品页面上并尝试关注 ProductHistory 链接时,URL 会更新并呈现,但组件数据不会更改。在 Codesandbox 示例中,您可以取消注释 Product components render 函数中的警报,以查看它在您点击链接时呈现,但没有任何反应。

我不知道问题是什么...您能解释一下问题并找到解决方案吗?那太好了!

【问题讨论】:

【参考方案1】:

除了componentDidMount,您还需要在Products 页面中实现componentWillReceiveProps 或使用getDerivedStateFromProps(从v16.3.0 开始),因为相同的组件是re-rendered,并更新了params 和@ 987654330@ 当您更改路由参数时,这是因为参数作为道具传递给组件并且在道具更改时,React 组件会重新渲染而不是重新安装。

编辑:从 v16.3.0 开始使用 getDerivedStateFromProps 设置/更新基于 props 的状态(无需在两种不同的生命周期方法中指定)

static getDerivedStateFromProps(nextProps, prevState) 
   if (nextProps.match.params.product !== prevState.currentProductId)
      const currentProductId = nextProps.match.params.product
      const result = productlist.products.filter(obj => 

        return obj.id === currentProductId;

      )
     return 

        product: result[0],
        currentId: currentProductId,
        result

      
  
  return null;

在 v16.3.0 之前,您将使用 componentWillReceiveProps

componentWillReceiveProps(nextProps) 
    if (nextProps.match.params.product !== this.props.match.params.product) 
      const currentProductId = nextProps.match.params.product
      const result = productlist.products.filter(obj => 

        return obj.id === currentProductId;

      )
      this.setState(

        product: result[0],
        currentId: currentProductId,
        result

      )
    
  

Working codesandbox

【讨论】:

componentWillReceiveProps 现在已弃用。但是你还能怎么做呢? @BastianVoigt :您可以在反应 +16.3 getDerivedStateFromPropscomponentDidUpdate 中使用新生命周期方法的集成,如 this answer 中所述。 我尝试了getDerivedStateFromProps 解决方案。但它没有用。 getDerivedStateFromProps 似乎只在装载时触发一次。 @arvinsim,不正确,getDerivedStateFromPRops 在初始挂载以及所有其他更新时触发。在 16.4 中,此 API 也发生了变化。我将更新我的答案以纳入该更改 这为我指明了正确的方向,感谢您的出色回答。【参考方案2】:

由于产品组件已经加载,它不会重新加载。您必须在组件的以下方法中处理新产品ID

componentWillReceiveProps(nextProps) 
if(nextProps.match.params.name.product == oldProductId)
  return;
else 
 //fetchnewProduct and set state to reload

使用最新版本的 react(16.3.0 以上)

static getDerivedStateFromProps(nextProps, prevState)
   if(nextProps.productID !== prevState.productID)
     return  productID: nextProps.productID;
   
  else 
     return null;
  


componentDidUpdate(prevProps, prevState) 
  if(prevProps.productID !== this.state.productID)
     //fetchnewProduct and set state to reload
  

【讨论】:

ComponentWillReceiveProps 也已被弃用,现在被认为不安全......还有其他方法吗? @BastianVoigt 它应该是 prevProps,因为 componentDidUpdate 将在 props 更改后调用,这将与我们在 getDerivedStateFromProps 中设置的 nextProps 相同。 对,但是当当前的 productID (this.props.productID) 与我的状态不同时,我想重新加载,你不觉得吗? @BastianVoigt。没错,这就是上述条件的作用。当前的 productID(this.props.producID) 将具有我们在状态变量中所说的新 porductID。如果我们比较它,那么它总是相等的【参考方案3】:

虽然上述所有方法都行得通,但我看不出使用getDerivedStateFromProps 的意义。 基于 React 文档,“如果您只想在 prop 更改时重新计算某些数据,请改用 memoization helper”。

在这里,我建议只使用componentDidUpdate 并将Component 更改为PureComponenet

参考 React 文档,PureComponenets 仅在至少一个状态或道具值发生变化时重新渲染。更改是通过对 state 和 prop 键进行浅层比较来确定的。

  componentDidUpdate = (prevProps) => 
    if(this.props.match.params.id !== prevProps.match.params.id ) 
      // fetch the new product based and set it to the state of the component
   ;
  ;

请注意,上述方法仅在将 Component 更改为 PureComponent 时有效,显然,您需要从 React 中导入它。

【讨论】:

就我而言,它也适用于常规组件。【参考方案4】:

如果您不在组件中维护状态,则可以使用 componentDidUpdate 而无需 getDerivedStateFromProps

  componentDidUpdate(prevProps) 
    const  match:  params:  value    = this.props
    if (prevProps.match.params.value !== value)
      doSomething(this.props.match.params.value)
    
  

【讨论】:

以上是关于React 不会在路由参数更改或查询更改时重新加载组件数据的主要内容,如果未能解决你的问题,请参考以下文章

更改 url 参数时反应路由器不重新加载组件

如果路由到相同的参数,角度不会重新加载

React路由器参数更改时强制重新安装组件?

React 页面不会在 url 查询更改时重新呈现

如果没有在 URL 更改上重新加载页面,React 不会呈现

React:如何停止重新渲染组件或对同一路由更改路由器进行 API 调用