React v16.3 生命周期

变更的部分

react v16.3终于出来了,最大的变动莫过于生命周期去掉了以下三个

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

同时为了弥补失去上面三个周期的不足又加了两个

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

当然,这个更替是缓慢的,在整个16版本里都能无障碍的使用旧的三生命周期,但值得注意的是,旧的生命周期(unsafe)不能和新的生命周期同时出现在一个组件,否则会报错“你使用了一个不安全的生命周期”。

为什么要改

旧的生命周期十分完整,基本可以捕捉到组件更新的每一个state/props/ref,没有什从逻辑上的毛病。

但是架不住官方自己搞事情,react打算在17版本推出新的Async Rendering,提出一种可被打断的生命周期,而可以被打断的阶段正是实际dom挂载之前的虚拟dom构建阶段,也就是要被去掉的三个生命周期。

生命周期一旦被打断,下次恢复的时候又会再跑一次之前的生命周期,因此componentWillMount,componentWillReceiveProps, componentWillUpdate都不能保证只在挂载/拿到props/状态变化的时候刷新一次了,所以这三个方法被标记为不安全。

两个新生命周期

static getDerivedStateFromProps

  • 触发时间:在组件构建之后(虚拟dom之后,实际dom挂载之前) ,以及每次获取新的props之后。
  • 每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state.
  • 配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法
class Example extends React.Component {
  static getDerivedStateFromProps(nextProps, prevState) {
    // 没错,这是一个static
  }
}

getSnapshotBeforeUpdate

  • 触发时间: update发生的时候,在render之后,在组件dom渲染之前。
  • 返回一个值,作为componentDidUpdate的第三个参数。
  • 配合componentDidUpdate, 可以覆盖componentWillUpdate的所有用法。
class Example extends React.Component {
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // ...
  }
}

建议用法总结

  1. 初始化state — Initializing state

    • 在constructor初始化state就可以了
  2. 请求数据 — Fetching external data

    • 在componentDidMount请求异步加载的数据
    • 有一种错觉,在componentWillMount请求的数据在render就能拿到,但其实render在willMount之后几乎是马上就被调用,根本等不到数据回来,同样需要render一次“加载中”的空数据状态,所以在didMount去取数据几乎不会产生影响。
  3. 添加事件监听 — Adding event listeners (or subscriptions)

    • 在componentDidMount中添加加事件监听
    • react只能保证componentDidMount-componentWillUnmount成对出现,componentWillMount可以被打断或调用多次,因此无法保证事件监听能在unmount的时候被成功卸载,可能会引起内存泄露
  4. 根据props更新state — Updating state based on props

    • 用getDerivedStateFromProps(nextProps, prevState), 将传入的props更新到state上
    • 用来代替componentWillReceiveProps(nextProps, nextState),willReceiveProps经常被误用,导致了一些问题,因此该方法将不被推荐使用。
    • getDerivedStateFromProps是一个static方法,意味着拿不到实例的this,所以想要在setState之前比对一下props有没有更新,下面方法是不能用了
     if (this.props.currentRow !== nextProps.currentRow) {
        ...
     }
    
    

    取而代之的是,额外写一个state来记录上一个props (` ^ ‘)

    if (nextProps.currentRow !== prevState.lastRow) {
      return {
        ...
        lastRow: nextProps.currentRow,
      };
      // 不更新state
      return null
    }
    
    
    • 为什么我们不给一个prevProps参数呢,官方解释是,一来prevProps第一次被调用的时候是null,每次更新都要判断耗性能,二来如果大家都习惯了,以后react不记录prevProps的话(啥),可以省下不少内存
  5. 触发请求 — Invoking external callbacks

    • 在生命周期中由于state的变化触发请求,在componentDidUpdate中进行
    • 为什么不在componentWillUpdate中的理由同上2
  6. props更新引起的副作用 — Side effects on props change

    • props更改引发的可视变化(副作用,比如log,ga),在componentDidUpdate中处理
    // 在didUpdate中根据props更新的确很不适应
    // props变了也是可以触发update的
    componentDidUpdate(prevProps, prevState) {
        if (this.props.isVisible !== prevProps.isVisible) {
          logVisibleChange(this.props.isVisible);
        }
    }
    
    
    • componentWillUpdate, componentWillReceiveProps在一次更新中可能会被触发多次,因此这种只希望触发一次的副作用应该放在保证只触发一次的componentDidUpdate中。
  7. props更新时重新请求 — Fetching external data when props change

    • 传入新的props时重新异步取数据,getDerivedStateFromProps+ componentDidUpdate 替代 componentWillReceiveProps
    // old
      componentWillReceiveProps(nextProps) {
        if (nextProps.id !== this.props.id) {
            this.setState({externalData: null});
          this._loadAsyncData(nextProps.id);
        }
      }
    
    
    // new
      static getDerivedStateFromProps(nextProps, prevState) {
        // Store prevId in state so we can compare when props change.
        if (nextProps.id !== prevState.prevId) {
          return {
            externalData: null,
            prevId: nextProps.id,
          };
        }
        // No state update necessary
        return null;
      }
      componentDidUpdate(prevProps, prevState) {
        if (this.state.externalData === null) {
          this._loadAsyncData(this.props.id);
        }
      }
    
    
  8. 在更新前记录原来的dom节点属性 — Reading DOM properties before an update

    • 在upate之前获取dom节点,getSnapshotBeforeUpdate(prevProps, prevState)代替componentWillUpdate(nextProps, nextState)
    • getSnapshotBeforeUpdate在render之后,但在节点挂载前
    • componentDidUpdate(prevProps, prevState, snapshot)直接获得getSnapshotBeforeUpdate返回的dom属性值

生命周期功能替换一览

  static getDerivedStateFromProps(nextProps, prevState) {
    4. Updating state based on props
    7. Fetching external data when props change
  }
  constructor() {
    1. Initializing state
  }
  componentWillMount() {
    // 1. Initializing state
    // 2. Fetching external data
    // 3. Adding event listeners (or subscriptions)
  }
  componentDidMount() {
    2. Fetching external data
    3. Adding event listeners (or subscriptions)
  }
  componentWillReceiveProps() {
    // 4. Updating state based on props
    // 6. Side effects on props change
    // 7. Fetching external data when props change
  }
  shouldComponentUpdate() {
  }
  componentWillUpdate(nextProps, nextState) {
    // 5. Invoking external callbacks
    // 8. Reading DOM properties before an update

  }
  render() {
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    8. Reading DOM properties before an update
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
    5. Invoking external callbacks
    6. Side effects on props change
  }

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

推荐阅读更多精彩内容