如何在重新渲染期间从道具更新派生状态

Posted

技术标签:

【中文标题】如何在重新渲染期间从道具更新派生状态【英文标题】:How to update derived state from props during rerender 【发布时间】:2015-10-26 14:19:54 【问题描述】:

我有一个组件:

React = require 'react'

module.exports = Toolbar = React.createClass

  proptypes:
    initialUrl: React.PropTypes.string
    onUrlChange: React.PropTypes.func

  getInitialState: ->
    url: @props.initialUrl

  handleUrlChange: (e) ->
    @setState(url: e.target.value)

  handleUrlKeyUp: (e) ->
    if e.which == 13
      e.target.blur()
      if (url = @state.url.trim())? and url isnt @props.initialUrl
        @props.onUrlChange?(url)
        @setState url
    if e.which == 27
      @setState url: @props.initialUrl



  render: ->
    <div className="component-toolbar clearfix">
      <input type="text" value=@state.url onChange=@handleUrlChange onKeyUp=@handleUrlKeyUp/>
    </div>

组件的工作方式如下:

    它在内部呈现带有initialUrl 的输入。 用户可以更改它。 当用户敲击回车时,触发事件onUrlChange。 当用户按下 esc 时,@state.url 恢复为原始值 (initialUrl)

到目前为止一切顺利。

但是当我更改上组件的道具或状态时,从 initalUrl 派生然后组件具有相同的状态并且不会重新渲染。这是因为 getInitialState 只被调用一次。但是解决这个问题的正确方法是什么?

谢谢。

【问题讨论】:

试试componentWillReceiveProps(nextProps)生命周期函数。 是的,这是一个选项。我试过了,没关系,但它看起来有点丑(hacky)。只是想知道什么是正确的方法。我猜这一定是一个常见的问题。 使用 componentWillReceiveProps 是正确的方法。为什么你认为它很hacky? 【参考方案1】:

如果您遵循 react 中的输入与 initialValue 一起使用的方式,则您当前的代码具有相同的行为。只有重新挂载会导致它更改 url。

作为替代方案,让父母管理状态并添加 onSubmit 事件怎么样?

module.exports = Toolbar = React.createClass

  propTypes:
    url: React.PropTypes.string
    onChange: React.PropTypes.func
    onSubmit: React.PropTypes.func
    onReset: React.PropTypes.func

  handleUrlChange: (e) ->
    @props.onChange(e.target.value)

  handleUrlKeyUp: (e) ->
    url = @props.url.trim()
    if e.which == 13
      e.target.blur()
      if url?
        @props.onChange?(url)
        @props.onSubmit?(url)
    if e.which == 27
      @props.onReset()

  render: ->
    <div className="component-toolbar clearfix">
      <input type="text" value=@props.url onChange=@handleUrlChange onKeyUp=@handleUrlKeyUp/>
    </div>

当然这会改变你的组件的 api,但不要害怕!因为您从一个灵活的无状态组件开始,所以您可以将其包装在一个覆盖 90% 用例的有状态组件中(例如 es6/7 中的示例)。

class StatefulToolbar extends React.Component 
  static propTypes = 
    initialUrl: React.PropTypes.string,
    onUrlChange: React.PropTypes.func
  ;

  constructor(props)
    super();
    this.state = url: props.initialUrl;
  

  render()
    return (
      <Toolbar 
        value=this.state.url
        onChange=(url) => this.setState(url)
        onReset=() => this.setState(url: this.props.initialUrl)
        onSubmit=(url) => this.props.onUrlChange(url)
      />
    );
  

如果您想控制基于其他事件的值更新,您应该为该特定情况创建一个包装器,而不是使工具栏变得复杂。

旁注:propTypes,而不是 proptypes。

【讨论】:

以上是关于如何在重新渲染期间从道具更新派生状态的主要内容,如果未能解决你的问题,请参考以下文章

当父组件在 Next.js 应用程序中更新其道具时重新渲染 React 子组件

防止子级在更新父级状态时重新渲染,使用父级方法作为本机反应的道具

使用重新选择计算派生状态时如何避免 React 重新渲染

在 React 中按下按钮时重新渲染道具数据

将新的商店状态放入道具后,反应不会重新渲染

如何通过使用功能组件更新 React 中的状态来重新渲染页面。?