更新道具的状态在React Form中更改

Dav*_*lla 152 reactjs

我在使用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
Run Code Online (Sandbox Code Playgroud)

Bra*_*ugh 269

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

React有一些处理这种情况的技巧.(注意,这是一篇旧文章,已经从网上删除.这是一个关于组件道具的当前文档的链接).

使用道具生成状态getInitialState通常会导致"真实来源"的重复,即真实数据的位置.这是因为getInitialState仅在首次创建组件时调用.

在可能的情况下,即时计算值以确保它们以后不会失去同步并导致维护问题.

基本上,无论何时将父项指定给props子项state,都不会在prop update上调用render方法.您必须使用该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 });
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 从React 16开始不推荐使用 (70认同)
  • 那么,不赞成使用componentWillReceiveProps之后的新方法应该是什么? (12认同)
  • @Boris 现在反应团队基本上是在告诉你要吃饱。他们为您提供了一个新方法,称为 getDerivedStateFromProps。问题是这是一个静态方法。这意味着您无法执行任何异步操作来更新状态(因为您必须立即返回新状态),也无法访问类方法或字段。您还可以使用记忆功能,但这并不适合所有用例。React 团队再次想要改变他们的做事方式。这是一个极其愚蠢且无能的设计决策。 (8认同)
  • @dude它还没有被弃用,你所指的只是一个未来的参考.我引用`[..]将来会被弃用 (7认同)
  • @poepje它可能尚未被弃用,但它被当前标准视为不安全,应该可以避免 (7认同)
  • @dude什么是弃用的?此代码仍可在React 16中使用. (3认同)
  • @poepje请参阅https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops (2认同)

Eri*_*ulz 60

显然事情正在改变.... 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
  }
}
Run Code Online (Sandbox Code Playgroud)

(上面代码来自danburzo @ github)

  • 仅供参考,你需要返回'null`如果没有什么应该改变那么就在你的if之后,你应该去`return null` (5认同)
  • getDerivedStateFromProps被强制为静态。这意味着您无法执行任何异步操作来更新状态,也无法访问类方法或字段。反应团队再次想压制他们的处事方式。这是一个非常愚蠢且无能为力的设计决策。 (2认同)

Luc*_*cia 28

componentWillReceiveProps 正在被弃用,因为使用它"经常会导致错误和不一致".

如果某些内容发生变化,请考虑完全重置子组件key.

key子组件提供prop可确保无论何时key从外部更改值,都会重新呈现此组件.例如,

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>
Run Code Online (Sandbox Code Playgroud)

关于它的表现:

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

  • 那很棒,并且可以在React 16中完美地工作 (3认同)
  • 关键,秘密!如上所述,在 React 16 中完美运行 (2认同)

arm*_*6er 21

还有componentDidUpdate可用.

功能签名:

componentDidUpdate(prevProps, prevState, snapshot)
Run Code Online (Sandbox Code Playgroud)

将此作为在更新组件时对DOM进行操作的机会.在初始时不会被调用render.

你可能不需要衍生州的文章,描述反模式两个componentDidUpdategetDerivedStateFromProps.我发现它非常有用.

  • 我最终使用“componentDidUpdate”,因为它很简单并且更适合大多数情况。 (2认同)

Gho*_*jad 11

你可能不需要派生状态

1.从父级设置一个键

当一个键发生变化时,React 会创建一个新的组件实例而不是更新当前的组件实例。键通常用于动态列表,但在这里也很有用。

2. 使用 getDerivedStateFromProps/componentWillReceiveProps

如果密钥由于某种原因不起作用(也许该组件的初始化成本非常高)

通过使用getDerivedStateFromProps您可以重置状态的任何部分,但此时(v16.7)似乎有点问题!,请参阅上面的链接以了解用法


小智 7

// store the startTime prop in local state\nconst [startTime, setStartTime] = useState(props.startTime)\n// \nuseEffect(() => {\n  if (props.startTime !== startTime) {\n    setStartTime(props.startTime);\n  }\n}, [props.startTime]);\n
Run Code Online (Sandbox Code Playgroud)\n

这个方法可以迁移到类components\xef\xbc\x9f吗

\n


小智 6

新的挂钩方法是使用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 });
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

我们使用setState设置状态,使用useEffect来检查对指定道具的更改,并采取措施在道具更改时更新状态。