React v16.8 发布了 Hooks,其主要是解决跨组件、组件复用的状态管理问题。
在 class 中组件的状态封装在对象中,然后通过单向数据流来组织组件间的状态交互。这种模式下,跨组件的状态管理变得非常困难,复用的组件也会因为要兼容不同的组件变得产生很多副作用,如果对组件再次拆分,也会造成冗余代码增多,和组件过多带来的问题。
后来有了 Redux 之类的状态管理库,来统一管理组件状态。但是这种分层依然会让代码变得很复杂,需要更多的嵌套、状态和方法,写代码时也常在几个文件之间不停切换。hooks 就是为了解决以上这些问题。
文章不对 hooks 做太多详细介绍,建议阅读此文前,先到官网做大概的了解。此文基于上一篇文章《实现ssr服务端渲染》的代码,进行 hooks 改造。代码已经提交到仓库的 hooks 分支中,仓库链接 https://github.com/zimv/react-ssr/tree/hooks。
面向对象编程和函数式编程
在了解 hooks 的过程中,慢慢的感觉到了面向对象和函数式编程的区别。
在 class 模式中状态和属性方法等被封装在组件内,组件之间是相互以完整对象个体做交互,状态的修改需要在对象内部的 setState 中处理。
而 hooks 模式中,一切皆函数,也就是 hooks,可以被拆分成很多小单元再进行组合,修改状态的是一个 set 方法,此方法可以在任何其他的 hooks 中出现和调用。 class 更属于面向对象编程,而 hooks 更属于函数式编程。
React 也并不会移除 class,而是引入 hooks 使开发者能根据场景做更好的选择。它们依旧会在未来保持应有的迭代。
变化
使用 hooks 之后,原本的生命周期概念就会有所变化了。比如我们定义一个 hooks 组件 Index, 当组件运行时,Index 函数的调用就是一次 render,那么我们第一次 render 相当于原来的 willMount,而 useEffect 会在第一次 render 以后执行。官网文档也说过你可以把 useEffect Hooks 视作 componentDidMount、componentDidUpdate 和 componentWillUnmount 的结合。 state 也被 useState 替代,useState 传入初始值并返回变量和修改变量的 set 方法。
在我们服务端渲染的时候,上篇文章说过生命周期只会执行到 willMount 后的第一次 render。 那在我们 hooks 模式下,服务端渲染会执行 Index hooks 第一次 render,而 useEffect 不会被执行。
function Index(props){ console.log('render'); const [desc, setDesc] = useState("惹不起"); useEffect(() => { console.log('effect') }) return (<div>{desc}</div>) }