内置 Hooks(1):如何保存组件状态和使用生命周期?
useState:让函数组件具有维持状态的能力
- 在一个组件的多次渲染期间 useState 创建的 state 是共享的
- setState 会触发组件的重新渲染
什么样的值应该保存为 state
我们要遵循的一个原则就是:state 中永远不要保存可以通过计算得到的值。
值发生改变的时候需要重新渲染时,就需要使用 state 存储这个值了
比如说:
从 props 传递过来的值。有时候 props 传递过来的值无法直接使用,而是要通过一定的计算后再在 UI 上展示,比如说排序。
从 URL 中读到的值。比如有时需要读取 URL 中的参数,把它作为组件的一部分状态。那么我们可以在每次需要用的时候从 URL 中读取,而不是读出来直接放到 state 里。
从 cookie、localStorage 中读取的值。通常来说,也是每次要用的时候直接去读取,而不是读出来后放到 state 里。
info
在将 Angular 组件迁移到 React 的过程中,一般的原则就是:
- 控制模版渲染的属性需要作为 state
- 其他的属性一般都是使用 useRef(其实就是相当于类中的 this)
不过,state 虽然便于维护状态,但也有自己的弊端。一旦组件有自己状态,意味着组件如果重新创建,就需要有恢复状态的过程,这通常会让组件变得更复杂。
比如一个组件想在服务器端请求获取一个用户列表并显示,如果把读取到的数据放到本地的 state 里,那么每个用到这个组件的地方,就都需要重新获取一遍。
而如果通过一些状态管理框架,那么组件本身就可以是无状态的。无状态组件可以成为更纯粹的表现层,没有太多的业务逻辑,从而更易于使用、测试和维护。
useEffect:执行副作用
什么是副作用呢?通常来说,副作用是指一段和当前执行结果无关的代码。比如说要修改函数外部的某个变量,要发起一个请求,等等。也就是说,在函数组件的当次执行过程中,useEffect 中代码的执行是不影响渲染出来的 UI 的。?????
useEffect 是每次组件 render 完后判断依赖并执行就可以了(浏览器渲染完毕之后异步执行)
ComponentDidMount/ComponentDidUpdate 是在 DOM 更新完毕页面渲染之前同步执行
理解 Hooks 的依赖
Hooks 提供了让你监听某个数据变化的能力。
那么在定义依赖项时,我们需要注意以下三点:
依赖项中定义的变量一定是会在回调函数中用到的,否则声明依赖项其实是没有意义的。
依赖项一般是一个常量数组,而不是一个变量。因为一般在创建 callback 的时候,你其实非常清楚其中要用到哪些依赖项了。
React 会使用浅比较来对比依赖项是否发生了变化,所以要特别注意数组或者对象类型。如果你是每次创建一个新对象,即使和之前的值是等价的,也会被认为是依赖项发生了变化。这是一个刚开始使用 Hooks 时很容易导致 Bug 的地方。
掌握 Hooks 的使用规则
因而 Hooks 的使用规则包括以下两个:只能在函数组件的顶级作用域使用;只能在函数组件或者其他 Hooks 中使用。
Hooks 只能在函数组件的顶级作用域使用
所谓顶层作用域,就是 Hooks 不能在循环、条件判断或者嵌套函数内、return 语句的后面执行,而必须是在顶层。这样就可以保证在多次渲染中 hooks 的调用顺序是一致的。
原因是在 React 的内部,hooks 的顺序是通过一个链表来维护的,每执行完一个 hook,指针都会向下移动。
所以 Hooks 的这个规则可以总结为两点:
第一,所有 Hook 必须要被执行到。
第二,必须按顺序执行。
Hooks 只能在函数组件或者其它 Hooks 中使用
但是如果一定要在 Class 组件中使用,那应该如何做呢?其实有一个通用的机制,那就是利用高阶组件的模式,将 Hooks 封装成高阶组件,从而让类组件使用。