在 React 表单中更新props更改状态

IT技术 reactjs
2021-04-22 07:36:09

我在 React 表单和正确管理状态方面遇到问题。我有一个表单中的时间输入字段(在模态中)。初始值设置为 中的状态变量getInitialState,并从父组件传入。这本身工作正常。

当我想通过父组件更新默认的 start_time 值时,问题就出现了。更新本身通过setState start_time: new_time. 但是在我的表单中,默认start_time值永远不会改变,因为它只在getInitialState.

我试图通过componentWillUpdate强制改变状态setState start_time: next_props.start_time,这确实有效,但它给了我Uncaught RangeError: Maximum call stack size exceeded错误。

所以我的问题是,在这种情况下更新状态的正确方法是什么?我是不是在以某种方式考虑这个错误?

当前代码:

@ModalBody = React.createClass
  getInitialState: ->
    start_time: @props.start_time.format("HH:mm")

  #works but takes long and causes:
  #"Uncaught RangeError: Maximum call stack size exceeded"
  componentWillUpdate: (next_props, next_state) ->
    @setState(start_time: next_props.start_time.format("HH:mm"))

  fieldChanged: (fieldName, event) ->
    stateUpdate = {}
    stateUpdate[fieldName] = event.target.value
    @setState(stateUpdate)

  render: ->
    React.DOM.div
      className: "modal-body"
      React.DOM.form null,
        React.createElement FormLabelInputField,
          type: "time"
          id: "start_time"
          label_name: "Start Time"
          value: @state.start_time
          onChange: @fieldChanged.bind(null, "start_time")

@FormLabelInputField = React.createClass
  render: ->
    React.DOM.div
      className: "form-group"
      React.DOM.label
        htmlFor: @props.id
        @props.label_name + ": "
      React.DOM.input
        className: "form-control"
        type: @props.type
        id: @props.id
        value: @props.value
        onChange: @props.onChange
6个回答

componentWillReceiveProps被depcricated因为react16:使用getDerivedStateFromProps代替

如果我理解正确,您有一个父组件传递start_timeModalBody组件,组件将其分配给自己的状态?并且您想从父组件而不是子组件更新该时间。

React 有一些处理这种情况的技巧。(请注意,这是一篇已从网络上删除的旧文章。这是当前组件 props 文档的链接)。

使用props生成状态getInitialState通常会导致“真实数据来源”的重复,即真实数据在哪里。这是因为getInitialState仅在首次创建组件时调用。

只要有可能,即时计算值以确保它们以后不会失去同步并导致维护麻烦。

基本上,每当您将父级分配给props子级时state,渲染方法并不总是在props更新时调用。您必须使用该componentWillReceiveProps方法手动调用它

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}
@poepje 它可能还没有被弃用,但按照当前的标准,它被认为是不安全的,应该避免
2021-05-23 07:36:09
@dude 它还没有被弃用,你所指的只是一个提醒以备将来参考。我引用[..]going to be deprecated in the future
2021-06-05 07:36:09
从 React 16 开始弃用
2021-06-08 07:36:09
那么,在 componentWillReceiveProps 被弃用后,新的方法应该是什么?
2021-06-13 07:36:09
@Boris 现在反应团队基本上是告诉你要吃饱了。他们为您提供了一个新方法,称为 getDerivedStateFromProps。问题是这是一个静态方法。这意味着您不能异步执行任何操作来更新状态(因为您必须立即返回新状态),您也不能访问类方法或字段。您也可以使用记忆功能,但这并不适合所有用例。再一次,react 团队想要强制降低他们的做事方式。这是一个极其愚蠢和无能的设计决定。
2021-06-17 07:36:09

显然事情正在发生变化...... getDerivedStateFromProps()现在是首选函数。

class Component extends React.Component {
  static getDerivedStateFromProps(props, current_state) {
    if (current_state.value !== props.value) {
      return {
        value: props.value,
        computed_prop: heavy_computation(props.value)
      }
    }
    return null
  }
}

(以上代码由 danburzo @ github 提供)

getDerivedStateFromProps 强制为静态。这意味着您不能异步执行任何操作来更新状态,也不能访问类方法或字段。再一次,react 团队想要强制降低他们的做事方式。这是一个极其愚蠢和无能的设计决定。
2021-05-28 07:36:09
@IlgıtYıldırım - 已经编辑了代码,因为有 4 人对你的评论投了赞成票 - 这真的有什么不同吗?
2021-06-07 07:36:09
仅供参考,null如果在你之后没有任何变化,你也需要返回,你应该去return null
2021-06-09 07:36:09
有一个很好的资源,深入介绍了不同的选项,以及为什么要使用getDerivedStateFromPropsmemoization reactjs.org/blog/2018/06/07/...
2021-06-13 07:36:09

componentWillReceiveProps 正在被弃用,因为使用它“通常会导致错误和不一致”。

如果外部发生了变化,请考虑使用key.

key子组件提供prop 可确保每当key外部的值发生变化时,都会重新渲染此组件。例如,

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

关于它的表现:

虽然这听起来可能很慢,但性能差异通常是微不足道的。如果组件具有在更新时运行的大量逻辑,则使用键甚至可以更快,因为该子树绕过了差异。

@user3468806 如果它不是具有外部引用的复杂对象,您可以使用JSON.stringify(myObject)从您的对象派生唯一键。
2021-05-24 07:36:09
Key 确实适用于对象,我做到了。当然,我有一个唯一的密钥字符串。
2021-05-26 07:36:09
关键,秘密!如上所述在 React 16 中完美运行
2021-05-31 07:36:09
键将不起作用,如果它是一个对象并且您没有唯一的字符串
2021-06-04 07:36:09
即使key更改,也不会触发componentDidMount
2021-06-13 07:36:09

这样做的新钩子方法是使用 useEffect 而不是 componentWillReceiveProps 的旧方法:

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

在功能钩子驱动组件中变为以下内容:

// store the startTime prop in local state
const [startTime, setStartTime] = useState(props.startTime)
// 
useEffect(() => {
  if (props.startTime !== startTime) {
    setStartTime(props.startTime);
  }
}, [props.startTime]);

我们使用 setState 设置状态,使用 useEffect 我们检查指定props的更改,并在props更改时采取行动更新状态。

这正是我正在寻找的。完美地遵循模式。
2021-06-04 07:36:09
这不是反模式reactjs.org/blog/2018/06/07/...
2021-06-19 07:36:09

还有componentDidUpdate可用。

函数签名:

componentDidUpdate(prevProps, prevState, snapshot)

以此为契机,在组件更新后对 DOM 进行操作。不会在 initial 上被调用render

你可能不需要派生状态文章,它描述了componentDidUpdate和 的反模式getDerivedStateFromProps我发现它非常有用。

我很困惑,因为您分享的文章说不要采用这种方法使用密钥使组件无效?
2021-06-12 07:36:09
我最终使用componentDidUpdate它是因为它很简单,而且更适合大多数情况。
2021-06-13 07:36:09
请参阅结尾,“如果每个值都有明确的事实来源,则可以避免上述反模式”。它通常说,如果你结束了检查props,并prevPropsdidUpdate您重新考虑数据流和层次有可能是一个更好的解决方案。
2021-06-14 07:36:09