为啥在 setState 之后调用 getDerivedStateFromProps?
Posted
技术标签:
【中文标题】为啥在 setState 之后调用 getDerivedStateFromProps?【英文标题】:Why getDerivedStateFromProps is called after setState?为什么在 setState 之后调用 getDerivedStateFromProps? 【发布时间】:2018-12-03 19:41:21 【问题描述】:React 引入了新的静态方法getDerivedStateFromProps(props, state)
,它在每个渲染方法之前被调用,但是为什么呢?在道具更改后调用它对我来说是有意义的,但在 setState
之后它没有,也许我错过了一些东西。
我正在根据我公司的要求创建一个datePicker
组件,组件日期由道具控制。我在组件中有以下状态。
selectedDate: number;
selectedMonth: number;
selectedYear: number;
currentMonth: number;
currentYear: number;
view: string;
selected 代表选定的日期,它来自 date 属性,currentMonth
和 currentYear
代表当前日历视图中的月份和年份。
如果 date
来自 prop 更改 selected*
,currentMonth
和 currentYear
应相应更改。为此,我使用getDerivedStateFromProps
,但假设用户单击月份名称,这会将日历视图切换到月份(而不是显示月份的日期名称),该函数使用 setState 更新currentMonth
,但是日期道具与以前相同(包含上个月),但应该调用 getDerivedStateFromProps
并且 currentMonth 再次与以前相同而不是更改。
我在state
中创建了一个额外的变量来跟踪是否由于setState
而调用了getDerivedStateFromProps
,但我认为这不是正确的方法。
要么我做错了什么或遗漏了什么,要么getDerivedStateFromProps
不应该在setState
之后调用。可能我做错了什么。
【问题讨论】:
getDerivedStateFromProps 不会在 setState 调用中调用。当父级重新渲染调用子级 getDerivedStateFromProps 以及组件安装时。可重现的演示或相关代码可能有助于指出错误。查看这个演示,证明 setState 不会触发 getDerivedStateFromProps codesandbox.io/s/k94z83mz6r getDerivedStateFromProps 在每个渲染方法之前调用,它仅用于在道具更改时调用一次,但他们在可能的 16.4 版本中更改了它 你能再次检查沙箱吗,我已经更新了反应和反应- dom版本 是的,你是对的,在最新版本中,每次重新渲染之前都会调用它 那么您是否找到了解决方法。每次重新渲染时都调用它似乎没用,因为它破坏了 setState 功能,并且无法区分状态更改和道具更改。真烦人…… 嗨@tylik,我正在从构造函数中的道具设置状态,并在需要时使用 componentDidUpdate 从道具更新状态。 【参考方案1】:以下内容似乎对我很有效,而且很笼统。
在构造函数中:
this.state =
props,
// other stuff
在 gDSFP 中:
static getDerivedStateFromProps(newProps, state)
if (state.props !== newProps)
// Compute and return new state relevant to property change
else
return state;
一些实验证实“状态”不再自动是旧状态,而是在这个钩子实例中是新设置的状态与现有道具的组合。我想添加钩子是为了不必直接调用 gDSFP 来重复依赖于道具的复杂状态演变,例如
this.setState(ClassName.getDerivedStateFromProps(this.props, newState))
问题在于,这并不是一个重大变化,但如果旧代码在 getDerivedStateFromProps 中不必要地进行大量计算,则可能会导致效率低下。例如,我刚刚发现一个复杂的对象被三次构造,一次是真实的,另外两次只是因为这个新的钩子。
【讨论】:
【参考方案2】:对于这种情况(根据道具变化更新状态),使用:
componentDidUpdate(prevProps)
// don't forget to compare props
if (this.props.userID !== prevProps.userID)
this.fetchData(this.props.userID);
componentDidUpdate
将在每次更新后被调用(由于道具更改/状态更改)。所以你应该检查道具是否改变了(this.props.userID !== prevProps.userID
)。
【讨论】:
【参考方案3】:我做了这样的事情
constructor(props)
super(props);
this.state =
expanded: props.expanded,
ownUpdate: false
static getDerivedStateFromProps(props, state)
if (state.ownUpdate)
return
expanded: state.expanded,
ownUpdate: false
;
else if (props.expanded !== state.expanded)
return
expanded: props.expanded
;
return null;
toggle()
this.props.onAftePress(this.state.expanded, this.props.index);
this.setState(
expanded: !this.state.expanded,
ownUpdate: true
)
【讨论】:
是否有任何其他解决方案来代替设置 ownUpdate:true? 这对我来说看起来不安全。 React 可以批量渲染,因此可能会调用 getDerivedStateFromProps,因为您的状态已更改并且道具已更改。在这种特殊情况下,您最好存储 props.expanded 值中的 prevExpanded 状态并与之比较以查看扩展的 prop 是否已更改。【参考方案4】:每当收到 new props、setState 和 forceUpdate 时,getDerivedStateFromProps
挂钩的工作方式。
在16.3
的版本中,无论何时使用setState
,React 都不会影响getDerivedStateFromProps
。但是他们在以16.4
开头的版本中对其进行了改进,因此每当setState
被调用时,getDerivedStateFromProps
就会被挂钩。
这是从React lifecycle diagram提取的图像:
16.3
^16.4
因此,何时通过正确检查道具和状态来挂钩 getDerivedStateFromProps
取决于您。这是一个例子:
static getDerivedStateFromProps (props, state)
// check your condition when it should run?
if(props.currentMonth != state.currentMonth)
return
currentMonth: state.currentMonth
// otherwise, don't do anything
else
return null
【讨论】:
在 16.4 版中改进了吗?更像是他们在 16.4 版中破坏了它。新版本没用。 这是改进。你认为什么没用?【参考方案5】:您的问题本身就有答案。 “在每个渲染方法之前调用”。每当您执行setState
时,都会调用render
方法。
我建议您将状态变量 currentMonth
和 currentYear
提升到父组件,并将它们与其他三个一起作为 prop 传递。您还可以将更改处理程序作为道具传递并从子级调用它。
在初始render
-
currentMonth
和currentYear
可以设置为null
,这样您就可以拥有显示默认内容的逻辑。当有人知道月份名称时,您可以从父级调用changeHandler
,它将传递新的道具。现在在 getderivedstatefromprops
中,您不再将 currentMonth
和 currentYear
设为 `null,因此您知道月份已更改。
【讨论】:
【参考方案6】:我也遇到了这个问题。所以我设置了另一个变量来检查第一次收到的道具。
this.state=flag:true
在getderivedstatefromprops
static getderivedstatefromprops(props, state)
if(props.<*propName*> && flag)
return( props.<*propName*>, flag:false)
如果你想使用多个 props 值,你需要相应地设置你的 if 语句(或任何其他逻辑)。
【讨论】:
以上是关于为啥在 setState 之后调用 getDerivedStateFromProps?的主要内容,如果未能解决你的问题,请参考以下文章
Flutter:状态类:为啥不在 setState 方法调用之前改变状态变量,而不是在 setState 方法中
为啥 React setState hook 没有立即更新? [复制]