函数化的React Hooks

写在前面

在2018年React Conf大会上,React团队讲解了目前你们使用React开发过程存在的一些问题并推出了一个使人激动的概念:React Hooks,并介绍hooks如何解决这些问题。恰好我所在的团队前端使用的是react+ts+node这套技术栈,前段时间将react版本升级到了16.8,在这个过程当中使用到了hooks的功能。在这里写下本身的感觉和看法。前端

本文将从如下几部分进行总结:node

  1. React Hooks以前还存在哪些痛点?
  2. React Hooks带来的代码模式改变
  3. 总结
  4. 扩展阅读

React Hooks以前还存在哪些痛点?

1.组件间类似的逻辑复用难

在react hooks问世以前,react能提供给组件间复用逻辑采用的是mixin,但这种方式弊端很是多,因此你们基本都是用render props(渲染属性)和HOC(高阶组件)来复用组件间的逻辑。但这两种方式的缺点也是很是明显的,很容易产生「wrapper hell」,即组件很容易臃肿,增长了debugger的成本。react

2.生命周期的问题

在稍微复杂的业务逻辑当中,生命周期是绕不过去的问题。有不少场景下在componentDidmount处理的逻辑不得不又在componentDidUpdate从新处理一次,业务被分散在各个不一样的生命周期函数中。 编程

而Hooks的出现,从面向生命周期编程转变为面向业务逻辑编程 数组

3.对人以及机器都不友好的class

虽然Hooks以前能够编写纯函数组件,但它只能是无状态的。很是常见的状况是你编写了一个纯函数组件,但过一段时间你发现得加上一个状态,这时候不得不将纯函数组件改成类组件。这时候得处理this的问题,并且编译器对class也是不友好的。bash

React Hooks带来的代码模式改变

import { useState, useEffect } from 'react' 
function Example(props) {
  // 声明一个新的状态变量"count" 
  const [count, setCount] = useState(0);

  useEffect(() => {
    subscribe(props.number, setCount)
    return () => {
      unsubscribe(props.number)
    }
  })

  return <div>{count}</div>
}
复制代码

useState会接受一个初始值,而后会返回一个[状态,状态修改器]的二元组。每次从新渲染时,整个函数会从新执行,可是useState会记住上次的值。并且react是根据useState的调用顺序来记得状态归属的:app

const Example = () => {
    const [size, setSize] = useState({ width: 100, height: 100 });
    const [count, setCount] = useState(0);   
}
复制代码

每一次 Example 被渲染,都是第一次 useState 调用得到 size 和 setSize,第二次 useState 调用得到 count 和 setCount。函数

useEffect是用来处理反作用的,至关于之前的componentDidMount和componentDidUpdate。它能够返回一个函数,用来在组件卸载前调用。因此useEffect聚合了componentDidMount,componentDidUpdate和componentWillUnmount的操做。若是props.number没有改变,咱们是不但愿在useEffect订阅/取消订阅里从新执行的,为了实现这个操做,只须要传入一个参数便可。ui

useEffect(() => {
    subscribe(props.number, setCount)
    return () => {
      unsubscribe(props.number)
    }
  }, [props.number])
复制代码

它是一个数组,只要这个数组中的每个值都不发生变化,则useEffect不须要从新执行。this

在将react升级以后,生命周期函数迁移到函数组件能够按照如下方式来操做:

  • constructor: 使用 useState 来初始化 state。
  • getDerivedStateFromProps: 函数组件自己能够直接操做。
  • shouldComponentUpdate: 使用 React.memo。
  • render: 函数组件自己。
  • componentDidMount, componentDidUpdate, componentWillUnmount: 使用 useEffect 实现。

在react hooks开发的过程当中,共享代码会采起函数组件的形式。约定使用useXXX开头,想重用这些代码,在函数式组件经过调用useXXX便可。

例如咱们能够写这样一个useMountLog,在组件mount的时候打印出一条日志:

const useMountLog = (name) => {
    useEffect(() => {
        console.log(`${name} mounted`);    
    });
}
复制代码

这样一来,全部的函数式组件中均可以经过调用useMountLog来使用这个功能:

const Example = () => {
    useMountLog('Example')
}
复制代码

再好比能够经过写useWindowWidth这样一个自定义hook来反映当前窗口的宽度

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  });
  
  return width;
}
复制代码

这样就能够在其余函数式组件中使用:

function MyResponsiveComponent() {
  const width = useWindowWidth();
  return (
    <p>Window width is {width}</p>
  );
}
复制代码

在上边的例子能够看到,react hooks大大地减小咱们的代码量。而hooks只能在函数式组件中使用,因此能够预测虽然如今react仍然支持类组件,但之后类组件会慢慢得消亡。

总结

React Hooks的出现,将大大地减小react的代码量,解决原来使用react开发遇到的一些问题,也给开发带来了一些变化。包括:

  • react hooks将改变组件间重用代码的方式。使用自定义 hooks,没有 mixin 带来的混乱,没有 HOC 带来的层级深渊。
  • 从面向生命周期编程到面向业务逻辑编程
  • 再也不须要class,再也不须要关注this等问题

最后,引用 Dan Abramov 在React Conf 2018 上的演讲词结束本文:

“……我曾经疑惑,React 的 Logo 为何是一个原子?后来我想到了这个解释。咱们知道物质由原子组成,是原子的特性决定了物质的外观和行为。就像 React,你能够把用户视图拆成独立的组件,再像原子同样自由组合起来,是组件的特性决定了用户视图的行为。科学家们曾一度认为原子是不可分割的最小单位,直到发现了电子,一种原子内部更小的粒子。事实上,是电子的特征影响了原子的性质。我认为 hooks 就比如电子,与其说它是一个新特性,不如说是已知的 React 特性(state,context,生命周期)的更直接的展示形式,而这四年来咱们却一直对它视而不见。

若是盯着 React 的 logo 看的话,你会发现 hooks 其实一直都在。”

扩展阅读

Making Sense of React Hooks

React Today and Tomorrow and 90% Cleaner React

对React Hooks的一些思考

相关文章
相关标签/搜索