自 React 16.8 发布之后,在已有项目中,把 package.json 中的 react 和 react-dom 版本一升,就能够抄起 Hooks 开干了。笔者目前已经在项目中开始了实操,但不妨先总结下官方文档中一些值得梳理的点。javascript
也就是说,create 的叫法就不太符合初始渲染以后获取到的是「当前状态」这么一个事实了。html
每一个组件内部都有一个「内存格子」的列表,他们就是一些存放数据的 JS 对象,当咱们使用如 useState 的 Hooks 时,就会去读取当前的格子(或者在初始渲染的时候进行初始化),而后将指针移动到下一个 Hooks。这就是为何一个组件内部的多个 useState 都能获取到各自的局部状态。java
可是须要注意的是,这也是为何官方建议咱们要将 hooks 的调用顺序保持一致。react
其一,React 会在每次渲染完成后会调用 useEffect,若是使用传统的生命周期钩子的话,当咱们但愿每次 render 后执行某种反作用时,咱们不得不在 componentDidMount 和 componentDidUpdate 里都塞上相同的逻辑,带来冗余。所以,传统的生命周期是不能代替 useEffect 的。这一点可参考 React Class 生命周期。json
固然,相比较考虑 mount 和 update,只考虑 render 是要简单清晰很多。dom
其二,Hooks 让咱们能够基于逻辑而拆分代码,而不是基于生命周期。这一点很是重要,由于基于生命周期来拆分代码,势必让逻辑相关联的代码分散各处。使用 Hooks,咱们就能够按照咱们指定的顺序使用每个反作用。ide
是的,这是为了保证在 useEffect 中使用到的内部状态都是最新的。这样 useEffect 就很像是 render 的一部分了 —— 每次使用的 useEffect 都属于其对应的的 render。函数
不只如此,咱们在 useEffect 中 return 的方法,也即一般用来作取消订阅这类 cleanup 工做的,每次 render 后也都会执行一次新的反作用(准确的说会先走 return 的方法,再从新走一次 useEffect 中的方法),而毫不是 unmount 的时候才执行一次。这种模式会有更少的 bug。oop
什么样的 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
对 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 完后执行的,多作点工做可能会少点问题。
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() { // 👍 这样就不会破坏第一条原则 if (name !== '') { localStorage.setItem('formData', name); } });
Only Call Hooks from React Functions.
这条没什么说的,总之只在下面两处用 Hooks: