componentWillReceiveProps 与 getDerivedStateFromProps

12

對於componentWillReceiveProps和getDerivedStateFromProps的作用我還有些模糊。因為在使用getDerivedStateFromProps時,我遇到了一個問題:

// Component 
state = {
  myState: []
}

// Using this method works fine:

componentWillReceiveProps(nextProps) {
  this.setState({
    myState: nextProps.myPropsState
  })
}

// But using this method will cause the checkboxes to be readonly:

static getDerivedStateFromProps(nextProps,prevProps) {
  const { myPropsState: myState } = nextProps
  return {
    myState
  }
}

// And here's checkbox
<input type="checkbox" id={`someid`} 
 onChange={(e) => this.handleMethod(e, comp.myState)} 
 checked={myState.indexOf(comp.myState) > -1} />

React 版本:16.4.1


传递给getDerivedStateFromProps()的参数是当前的propsstate,而不是nextPropsprevProps。它应该根据当前的状态和属性返回下一个状态。请至少仔细阅读文档。 - trixn
这里不应该有影响,参数名称可以随意设置,但我只是写了一个比较将会接收到的道具。此外,我在这里想知道为什么当我使用派生状态时复选框是只读的? - Bhojendra Rauniyar
通过将参数命名为它们不代表的内容来比较它们有什么帮助呢?此外,从props无条件更新状态被认为是一种反模式。 - trixn
3个回答

7

getDerivedStateFromProps不是componentWillReceiveProps的直接替代品,纯粹是因为它在每次更新后被调用,无论是状态改变、属性改变还是父组件重新渲染。

然而,无论什么情况下,仅仅从getDerivedStateFromProps返回状态并不是正确的方法,您需要在返回值之前比较状态和属性。否则,随着每次更新,状态会被重置为属性,这个循环将继续下去。

根据文档

getDerivedStateFromProps在调用渲染方法之前被调用,对于初始挂载和后续更新都是如此。它应该返回一个对象来更新状态,或者返回null以不更新任何内容。

这个方法存在于极少数情况下,其中状态随时间而变化取决于属性的变化。例如,对于实现一个<Transition>组件来说非常方便,该组件比较其先前和下一个子元素,以决定应该动画哪些元素进入和退出。

派生状态会导致冗长的代码,并使您的组件难以思考。确保您熟悉更简单的替代方法:

如果您需要执行副作用(例如数据获取或动画)以响应属性的更改,请使用componentDidUpdate生命周期。

如果您想要在某个属性更改时重新计算一些数据,请使用memoization助手。

如果您想要在某个属性更改时“重置”一些状态,请考虑将组件完全控制使用键完全不受控制

P.S.请注意,getDerivedStateFromProps的参数是propsstate而不是nextPropsprevProps

为了根据属性更改进行更改,我们需要在状态中存储prevPropsState,以便检测更改。典型的实现如下:

static getDerivedStateFromProps(props, state) {
    // Note we need to store prevPropsState to detect changes.
    if (
      props.myPropsState !== state.prevPropsState
    ) {
      return {
        prevPropsState: state.myState,
        myState: props.myPropsState
      };
    }
    return null;
  }

1
谢谢您的回答。但是我仍然面临同样的问题。 - Bhojendra Rauniyar
如果 myState 是一个数组,那么直接检查 props.myPropsState !== state.prevPropsState 将会失败。你需要深入比较这个数组或使用不可变列表。@BhojendraNepal - Shubham Khatri
@ShubhamKhatri 应该使用从父组件传递的新“key”(或“id”,如果重新初始化组件很昂贵)来允许通过传递新的props从父组件重置状态。这在这里中都有描述。在每次渲染之前深度比较对象似乎不是一个好的解决方案。 - trixn
@trixn 处理这种情况的最佳方法是记忆化和使用不可变对象列表。晚上我有更多时间时,会更新答案并提供更多细节。 - Shubham Khatri
我不想使用记忆化技术。除了派生状态之外,还有其他解决方案吗? - Bhojendra Rauniyar
显示剩余6条评论

1

最终,我解决了我的问题。这是一个痛苦的调试过程:

// Child Component

// instead of this
// this.props.onMyDisptach([...myPropsState])

// dispatching true value since myPropsState contains only numbers
this.props.onMyDispatch([...myPropsState, true])

这是因为我有两个条件:1)在复选框更改时(组件)2)在重置按钮按下时(子组件)
当重置按钮被按下时,我需要重置状态。因此,在将状态分派给重置按钮的props时,我使用了一个布尔值来知道它是来自重置的更改。您可以使用任何您喜欢的东西,但需要跟踪它。
现在,在这个组件中,我通过调试控制台输出找到了componentWillReceiveProps和getDerivedStateFromProps之间的差异的一些提示。
// Component
static getDerivedStateFromProps(props, state) {
    const { myPropsState: myState } = props
    // if reset button is pressed
    const true_myState = myState.some(id=>id===true)
    // need to remove true value in the store
    const filtered_myState = myState.filter(id=>id!==true)
    if(true_myState) {
      // we need to dispatch the changes to apply on its child component
      // before we return the correct state
      props.onMyDispatch([...filtered_myState])
      return {
        myState: filtered_myState
      }
    }
    // obviously, we need to return null if no condition matches
    return null
  }

以下是我在控制台输出的结果:
  • 每当props更改时,getDerivedStateFromProps立即记录日志

  • 只有在子组件传播props更改后,componentWillReceiveProps才会记录日志

  • getDerivedStateFromProps不响应props更改(我指的是像示例代码中的dispatch更改)

  • componentWillReceiveProps响应props更改

  • 因此,在使用getDerivedStateFromProps时,我们需要向子组件提供更改。

我需要将正确的值粘贴到状态中,因为getDerivedStateFromProps处理所有更改,而componentWillReceiveProps仅处理子组件将更改分派给props。

顺便说一下,您可以使用自定义属性来检查是否已更改,并在getDerivedStateFromProps中更新该值,但出于某种原因,我必须调整此技术。

可能我的措辞有些混乱,但我希望你能理解。


列表中的元素通常应该是相同类型的。此外,您需要遍历整个列表才能找到该值。请注意,您在每次渲染时都要这样做,而在可能有99%的情况下没有boolean。为什么不只是传递另一个属性以及重置,然后检查它是否更改?例如,像在React博客文章中建议的那样使用自动递增计数器?这将更有效,更容易且更少出错。您写道“getDerivedStateFromProps不响应道道具变化”。这是不正确的。它会响应道具或状态的每个更改。 - trixn
啊,这对别人来说有点混淆。我的意思是使用componentWillReceiveProps不需要分发,但使用derived则需要我为更改分发。我同意您的建议,但出于某些原因,我使用了上述方法。 - Bhojendra Rauniyar

0

来自React文档:

请注意,无论原因如何,此方法在每次渲染时都会触发。这与UNSAFE_componentWillReceiveProps相反,后者仅在父级导致重新渲染时触发,而不是由于本地setState的结果。

每次调用setState()后,您实际上都会使用当前props覆盖状态。因此,当您选中一个复选框时,将调用(e) => this.handleMethod(e, comp.myState),假设它调用setState()以更新复选框的选中状态。但是在此之后,getDerivedStateFromProps()将被调用(在渲染之前),以撤消该更改。这就是为什么从props无条件更新状态被认为是一种反模式


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接