componentWillReceiveProps与getDerivedStateFromProps

时间:2018-08-03 11:26:08

标签: javascript reactjs lifecycle

对于我来说,究竟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} />

反应版本:16.4.1

3 个答案:

答案 0 :(得分:4)

getDerivedStateFromProps并不是componentWillReceiveProps的直接替代,纯粹是因为它在每次更新后都会被调用,无论是状态更改还是道具更改或父级重新呈现。 / p>

无论如何,仅从getDerivedStateFromProps返回状态是不正确的方法,您需要在返回值之前比较状态和props。否则,每次更新都会将状态重置为道具,并且循环会继续

按照 docs

  

getDerivedStateFromProps在调用渲染之前被调用   初始安装和后续更新上的方法。这应该   返回对象以更新状态,或返回null不更新任何内容。

     

此方法适用于状态取决于以下情况的罕见用例   道具随时间变化。例如,它可能对   实现一个<Transition>组件,将其先前的和   下一个孩子决定要对其中的一个进行动画处理。

     

派生状态导致冗长的代码并使您的组件   很难考虑。确保您熟悉更简单   替代方案:

     

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

     

如果您只想在道具更改时重新计算一些数据,请使用   而是memoization助手。

     

如果您想在道具更改时“重置”某些状态,请考虑   制作组件fully controlledfully uncontrolled with a key instead

PS 。请注意,getDerivedStateFromProps的参数是propsstate,而不是nextProps and prevProps`

要了解更多详细信息,

为了根据道具更改进行更改,我们需要将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 :(得分:0)

从react docs:

  

请注意,无论原因如何,都会在每个渲染器上触发该方法。这与UNSAFE_componentWillReceiveProps相反,setState仅在父级导致重新渲染时才触发,而不是由于本地setState()而触发。

每次调用(e) => this.handleMethod(e, comp.myState)后,您实际上都在用当前道具覆盖状态。因此,当您选中一个框setState()时,即假设调用getDerivedStateFromProps()以更新该复选框的选中状态。但是之后,(function(){ 'use strict'; angular.module('sampleapp') .factory('Base64', function () { var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; return { encode: function (input) { var output = ""; var chr1, chr2, chr3 = ""; var enc1, enc2, enc3, enc4 = ""; var i = 0; do { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); chr1 = chr2 = chr3 = ""; enc1 = enc2 = enc3 = enc4 = ""; } while (i < input.length); return output; }, decode: function (input) { var output = ""; var chr1, chr2, chr3 = ""; var enc1, enc2, enc3, enc4 = ""; var i = 0; // remove all characters that are not A-Z, a-z, 0-9, +, /, or = var base64test = /[^A-Za-z0-9\+\/\=]/g; if (base64test.exec(input)) { window.alert("There were invalid base64 characters in the input text.\n" + "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" + "Expect errors in decoding."); } input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); do { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } chr1 = chr2 = chr3 = ""; enc1 = enc2 = enc3 = enc4 = ""; } while (i < input.length); return output; } }; }); })(); 将被调用(渲染前),以还原该更改。这就是unconditionally updating state from props is considered an anti-pattern的原因。

答案 2 :(得分:0)

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

// 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 for reset按钮时,我使用了一个布尔值来知道这是对reset的更改。您可以使用任何喜欢的东西,但需要对其进行跟踪。

现在,在组件的此处,在调试控制台输出后,我发现了一些关于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
  }

这是我发现控制台输出结果的地方:

  • getDerivedStateFromProps每当道具更改时立即记录

  • componentWillReceiveProps仅在子传播道具更改后记录日志

  • getDerivedStateFromProps不响应道具更改(我的意思是如示例代码中的调度更改)

  • componentWillReceiveProps响应道具更改

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

在我需要的状态下粘贴真实值的过程,因为与componentWillReceiveProps不同,getDerivedStateFromProps处理所有更改,而componentWillReceiveProps仅处理子组件将更改分发给道具。

顺便说一句,您可以使用自定义属性来检查它是否已更改,并在getDerivedStateFromProps中更新其值,但是由于某些原因,我必须对该技术进行调整。

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