React Hooks 梳理

栏目: IOS · Android · 发布时间: 5年前

内容简介:自 React 16.8 发布以后,在已有项目中,把 package.json 中的 react 和 react-dom 版本一升,就可以抄起 Hooks 开干了。笔者目前已经在项目中开始了实操,但不妨先总结下官方文档中一些值得梳理的点。也就是说,create 的叫法就不太符合初始渲染之后获取到的是「当前状态」这么一个事实了。每个组件内部都有一个「内存格子」的列表,他们就是一些存放数据的 JS 对象,当我们使用如 useState 的 Hooks 时,就会去读取当前的格子(或者在初始渲染的时候进行初始化),

自 React 16.8 发布以后,在已有项目中,把 package.json 中的 react 和 react-dom 版本一升,就可以抄起 Hooks 开干了。笔者目前已经在项目中开始了实操,但不妨先总结下官方文档中一些值得梳理的点。

useState

为什么 useState 不叫 createState 呢?

  • 初始渲染时,useState 返回的是 initState
  • 下次渲染时,useState 返回的是 curState

也就是说,create 的叫法就不太符合初始渲染之后获取到的是「当前状态」这么一个事实了。

为什么 useState 不通过 this 也知道自己是哪个 Component 的状态?

每个组件内部都有一个「内存格子」的列表,他们就是一些存放数据的 JS 对象,当我们使用如 useState 的 Hooks 时,就会去读取当前的格子(或者在初始渲染的时候进行初始化),然后将指针移动到下一个 Hooks。这就是为什么一个组件内部的多个 useState 都能获取到各自的局部状态。

但是需要注意的是,这也是为什么官方建议我们 要将 hooks 的调用顺序保持一致

useEffect

和过去的生命周期有什么区别?

其一,React 会在每次渲染完成后会调用 useEffect,如果使用传统的生命周期钩子的话,当我们希望每次 render 后执行某种副作用时,我们不得不在 componentDidMount 和 componentDidUpdate 里都塞上相同的逻辑,带来冗余。因此,传统的生命周期是不能代替 useEffect 的。这一点可参考React Class 生命周期。

当然,相比较考虑 mount 和 update,只考虑 render 是要简单清晰不少。

其二,Hooks 让我们可以基于逻辑而拆分代码,而不是基于生命周期。这一点非常重要,因为基于生命周期来拆分代码,势必让逻辑相关联的代码分散各处。使用 Hooks,我们就可以按照我们指定的顺序使用每一个副作用。

传入的函数每次 render 都是新的?

是的,这是为了保证在 useEffect 中使用到的内部状态都是最新的。这样 useEffect 就很像是 render 的一部分了 —— 每次使用的 useEffect 都属于其对应的的 render。

不仅如此,我们在 useEffect 中 return 的方法,也即通常用来做取消订阅这类 cleanup 工作的,每次 render 后也都会执行一次新的副作用(准确的说会先走 return 的方法,再重新走一次 useEffect 中的方法),而绝不是 unmount 的时候才执行一次。这种模式会有更少的 bug。

什么样的 bug 呢?可以看官方文档的例子,大致就是说,如果我们订阅的人的 id 变了,就需要取消订阅然后重新订阅新的人。这样一来,如果在使用 class 做订阅这类处理时,就需要在 3 个生命周期(componentDidMount、componentDidUpdate、componentWillUnmount)里散布逻辑,即在 componentDidUpdate 补充上取消并重新订阅的逻辑!

如果用了 useEffect,这些东西根本不需要去考虑。整个过程如文档中给的例子一样依次执行:

function FriendStatus(props) {
  // ...
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // ...
}

// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);     // Run first effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange);     // Run next effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange);     // Run next effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect
复制代码

useEffect 第二个参数的优化作用

对 return 的 cleanup 同样适用,不要忘了,每次 render 完就会先执行一次 cleanup,最终 unmount 的时候也会执行一次 cleanup。

useEffect(() => {
  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return () => {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
}, [props.friend.id]); // 只会在 props.friend.id 变化的时候重新订阅
复制代码

如果我们不提供该参数,每次更新都会重新执行;如果只想 mount 和 unmount 的时候各执行一次,可指定 [],但这不是好的实践方式,考虑到 useEffect 都是在 render 完后执行的,多做点工作可能会少点问题。

Hooks 使用原则

Only Call Hooks at the Top Level. Don’t call Hooks inside loops, conditions, or nested functions.

这一条的原因是,Hooks 是通过调用顺序分配存放位置的,只有每次 run 的时候顺序保持一致,才能挨个取得正确的 useState、useEffect。比方说,如果我们把 Hooks 放到条件语句里,然后第一次 render 的时候每个都执行,第二次 render 却有一个 Hook 不执行,那么后面的对应就出错了。很好理解吧。

但如果我们一定要有条件的执行 useEffect 呢?我们可以 在 useEffect 内部加条件

useEffect(function persistForm() {
    // :+1: 这样就不会破坏第一条原则
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });
复制代码

Only Call Hooks from React Functions.

这条没什么说的,总之只在下面两处用 Hooks:

  • :white_check_mark: Call Hooks from React function components.
  • :white_check_mark: Call Hooks from custom Hooks.

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据库系统实现

数据库系统实现

加西亚-莫利纳(Hector Garcia-Molina)、Jeffrey D.Ullman、Jennifer Widom / 杨冬青、吴愈青、包小源 / 机械工业出版社 / 2010-5 / 59.00元

《数据库系统实现(第2版)》是斯坦福大学计算机科学专业数据库系列课程第二门课的教科书。书中对数据库系统实现原理进行了深入阐述,并具体讨论了数据库管理系统的三个主要成分——存储管理器、查询处理器和事务管理器的实现技术。此外,第2版充分反映了数据管理技术的新进展,对内容进行了扩充,除了在第1版中原有的“信息集成”一章(第10章)中加入了新的内容外,还增加了两个全新的章:“数据挖掘”(第11章)和“数据......一起来看看 《数据库系统实现》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具