React16 - 为什么你不需要 getDerivedStateFromProps

前言:

在 React 16.3 中, 新的生命周期函数被引入了, 即静态方法 getDerivedStateFromProps.

componentWillReceiveProps, componentWillMount, componentWillUpdate 这三个生命周期函数都被添加了 UNSAFE_ 不安全标记. React 提示我们不应该继续使用这些生命周期函数.

getDerivedStateFromProps 是一个口直体嫌的 API, 它的用法和 componentWillReceiveProps 几乎完全不一致. 它的名字揭示了它的用途, 可是它的使用体验却并不令人愉悦. 我们有更好的办法替代它.


为什么要使用 componentWillReceiveProps / getDerivedStateFromProps ?

在初始化组件数据时, 我们有时需要将组件接收的参数 props 中的数据添加到它的 state 中, 期望组件响应 props 的变化.

然而组件接收的 props 数据是只读的, 不可变的, 禁止修改的. 当组件接收了新的 props 时, constructor 函数中数据初始化行为并不会再次发生. 于是我们想要在 componentWillReceiveProps / getDerivedStateFromProps 生命周期函数中获取新的 props 并且调用 setState() 来更新数据.

实际场景

在如下图所示的界面中, 当用户点击了左侧面板中的编辑按钮后, 右侧的表单组件要能够根据用户点击的类目而变化. 同时表单输入域上的任何变化都会实时改变这个组件的数据(这个表单组件内部需要维护 state, 不是一个只接受 props 的完全受控组件) .



static getDerivedStateFromProps 和 componentWillReceiveProps 的显著区别

尽管 static getDerivedStateFromProps 看起来像是 UNSAFE_componentWillReceiveProps 的替代 API, 但是二者的触发阶段, 参数和可访问的数据都有很大的差异.

getDerivedStateFromProps 并不是 componentWillReceiveProps 的替代品.

触发机制:

  • UNSAFE_componentWillReceiveProps(nextProps) 在组件接收到新的参数时被触发.

当父组件导致子组件更新的时候, 即使接收的 props 并没有变化, 这个函数也会被调用.

UNSAFE_componentWillReceiveProps()is invoked before a mounted component receives new props.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed.

  • getDerivedStateFromProps(props, state) 会在每次组件渲染前被调用

getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates.

getDerivedStateFromProps 会在每次组件被重新渲染前被调用, 这意味着无论是父组件的更新, props 的变化, 或是组件内部执行了 setState(), 它都会被调用.

工作方式

  • UNSAFE_componentWillReceiveProps(nextProps):

参数是组件接收到的新的 props , 用于比对新的 props 和原有的 props, 用户需要在函数体中调用 setState() 来更新组件的数据.

If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.

  • static getDerivedStateFromProps(nextProps, currentState):

参数是组件接收到的新的 props 和组件当前的数据. 用户需要在这个函数中返回一个对象, 它将作为 setState() 中的 Updater 更新组件.

It should return an object to update the state, or null to update nothing.


为什么不应该使用 getDerivedStateFromProps

Deriving state leads to verbose code and makes your components difficult to think about.

充满了限制, 很难用

getDerivedStateFromProps 是一个静态方法, 是一个和组件自身"不相关"的角色. 在这个静态方法中, 除了两个默认的位置参数 nextProps 和 currentState 以外, 你无法访问任何组件上的数据.

相比起在 UNSAFE_componentWillReceiveProps(nextProps) 的函数体中直接比较 this.props 和 nextProps 上的字段. 你需要"绕一个弯" 去比较 nextProps 和 currentState.

示例代码:

UNSAFE_componentWillReceiveProps(nextProps){
    if (this.props.currentExercise.id !== nextProps.currentExercise.id){
        this.setState({...nextProps.currentExercise})
    }
}

  
static getDerivedStateFromProps(nextProps, prevState) {

    if (prevState.id !== nextProps.currentExercise.id) {
      return {...nextProps.currentExercise}
    }
    return null;
}

会被频繁地触发

无论是组件调用了 setState(), 接收的 props 发生了变化, 或是父组件的更新都会导致子组件上的 getDerivedStateFromProps被触发.

使用的时候必须非常小心

由于 getDerivedStateFromProps 会在 setState() 后被调用, 并且它的返回值会被用于更新数据. 这意味着你会在 setState() 之后触发 getDerivedStateFromProps, 然后可能意外地再次 "setState()".

getDerivedStateFromProps(nextProps) 函数中的第一个位置参数未必是 "新" 的 props. 在组件内调用了 setState() 时, getDerivedStateFromProps 会被调用. 但是此时的组件其实并没有获得 "新" 的 props, 是的, 这个 nextProps 的值和原来的 props 是一样的.

这就导致了我们在使用 getDerivedStateFromProps 时, 必须添加很多逻辑判断语句来处理 props 上的更新和 state 上的更新, 避免意外地返回了一个 Updater 再次更新数据, 导致数据异常.

点击这里查看示例


更优雅的做法

React 官方博客中提供了以下几种方案:

  1. 让表单控件变成完全受控组件, 不论是 onChange 处理函数还是 value 都由父组件控制, 这样用户无需再考虑这个组件 props 的变化和 state 的更新.
function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}
  1. 让表单控件变成完全不受控组件, 但是具有 key 属性.
    仍然用自身的数据来控制 value. 但是接收 props 中的某个字段作为 key 属性的值, 以此响应 props 的更新: 当 key 的值变化时 React 会替换 (reset)组件, 从而重新生成初始化数据.

When a key changes, React will create a new component instance rather than update the current one.

示例代码:

//组件内的代码
class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}
// 在父组件中接收 props 中的数据作为 key
<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>
  • 其他方法请参考 这里

参考

生命周期函数: https://reactjs.org/docs/react-component.html
You Probably Don't Need Derived State: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 8,422评论 1 33
  • 说在前面 关于 react 的总结过去半年就一直碎碎念着要搞起来,各(wo)种(tai)原(lan)因(le)。心...
    陈嘻嘻啊阅读 6,846评论 7 41
  • 1、什么是react React.js 是一个帮助你构建页面 UI 的库。React.js 将帮助我们将界面分成了...
    谷子多阅读 2,555评论 1 13
  • 40、React 什么是React?React 是一个用于构建用户界面的框架(采用的是MVC模式):集中处理VIE...
    萌妹撒阅读 1,005评论 0 1
  • 生命周期流程图简单如下: 组件让你把用户界面分成独立的,可重复使用的部分,并且将每个部分分开考虑。React.Co...
    Simple_Learn阅读 1,072评论 0 0