10 分钟了解 Hooks API

Hooks API 是 React 16.7.0-alpha.0 版本推出的 API,要注意的是只有这个版本才有,别的版本都没有,如果你在 16.7.0 正式版是没有的!我就是被坑了。

如果你用 create-react-app 来创建应用的,现在的版本会是 16.7.0,一定要改成 16.7.0-alpha.0 才能使用 Hooks API

useState

useState API 本质了为了使得函数式组件也能有自身状态而推出的 API。

以前的函数组件

假如我们要做一个简单的计数器,如果用以前的函数式组件是不行的,因为函数式组件是没有自身状态的。比如:

function App()l {
  let count = 0
  let add = function() {}
  return (
    <div>
      <div>{count}</div>
      <div><button onClick={add}>+1</button></div>
    </div>
   )
}

你可能说 count 不行么?因为每次 render 的时候这个函数就会被执行一次,那么就会执行 let count = 0 ,所以每次都会重置了 count,没用。

解决方法是用 class 组件,在 this.state 对象里放入 count

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }

    add = () => {
        this.setState({
            count: this.state + 1
        })
    }

    render() {
        return (
          <div>
            <div>App: {count}</div>
            <div>
              <button onClick={add}>+1</button>
            </div>
          </div>
        )
    }
}

烦不烦?写那么多个单词。所以 Hooks API 出现了,它可以给函数式组件拥有自身状态。

使用 useState

useState 的作用就是创建一个状态,可以是对象也可以是别的类型,还会创建一个修改这个状态的函数。上面的例子用新 API 可以写成这样:

function App() {
    // 状态和修改状态的函数
    const [count, setCount] = useState(0)

    // Handler
    const add = () => {
        setCount(count + 1)
    }

    return (
      <div>
        <div>App: {count}</div>
        <div>
          <button onClick={add}>+1</button>
        </div>
      </div>
    )
}

是不是感觉清爽很多了?没有什么 constructor, super, render 那么多东西,现在只需要我好好写一个函数就行了。

注意

  • 不能在函数式组件外面使用 useState,这个很好理解,毕竟定义的就是自己的状态当然在组件自己里使用
  • 数组里的名字可以改,可以是 const [x, y] = useState(0),你自己清楚 x, y 代表什么就好了

useEffects

这个 API 就有点难理解了,Effect 的翻译是影响,副作用。这里的 Effects,相当于你使用了别人的东西。

什么是 Effect

这里的 Effect 分两种:一种叫 Effects without Cleanup,就是说你拿别人的东西吃,吃完这个东西就没了,不用再去清理,因为东西就在你肚子里呀。另一种叫 Effects with Cleanup,比如你拿别人的书看,看完了还得将书要么扔了,要么还给别人,总之这本书你得处理。

那么 React Effects 有哪些呢?简单的如:DOM 修改,网络请求,订阅事件,这些都是使用了组件之外的资源,所以每一次的这些行为就是一个 Effect。

那为毛要搞一个 useEffects 呢?下面就分两种 Effects 情况来说说吧,同时讲下怎么使用 useEffects。

Effects with Cleanup

想像一个场景,每次更新 count 之后都要设置 document.title = count,你会怎么做。哎,这个简单,在上面的 add() 函数里直接改不就好了?但是假如这个 count 作为共享数据可以被所有组件修改的,而且有 minus(), divide(), multiple() 等方法去改这个值,那就要重复很多次 document.title = count

这时候我们可以总结出不管怎样去修改,反正就是修改后就要执行 document.title = count 代码嘛。即然是 count 值每次都修改就会导致这个组件重新渲染,那么那我在生命钩子componentDidUpdatecomponentDidMount 里去改 title不就好了?如:

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = { count: 0 }
    }

    componentDidMount() {
        document.title = this.state.count
    }
    componentDidUpdate() {
        document.title = this.state.count
    }
    
    render() {
        return (...)
    }
}

还是那个问题:烦不烦啊,还要我写两次。这时候 React 就推出了 useEffect,即每次渲染(包括 mounted 和 updated)后,即就是说每次执行 render 后,会回调一个函数,这个函数执行你要做的事情。

可以将上面的改写成这样。

function App (props) {
    useEffect(() => {
        document.title = count
    })
    return (...)
}

代码变成6行,舒服~。再回来看看 Effect 的定义,使用组件外的东西,对呀我们使用了 document 对象,这不是组件里的呀。好像也不太需要清理什么东西,因为就设置一个值嘛。所以这就叫 Effects without Cleanup。

Effects with Cleanup

有 without 就有 with,什么时候要 Cleanup 呢?那肯定是无端端创建东西的时候嘛,像垃圾回收一样,如果创建的东西不再需要了,那就清掉呗。React 里的例子是添加监听事件喽。

比如在一个 MyButton 组件开始的时候我要默认添加一个事件中心的监听回调,然后这个组件销毁的时候回收这个回调函数。使用生命钩子会写成这样:

class MyButton extends React.Component {
    constructor(props) {
        super(props)
        this.state = { isTriggered: false }
    }

    componentDidMount() {
        EventHub.on('Trigger event', this.state.isTriggered, () => {
            console.log('Do something')
        })
    }
    componentWillUnmount() {
        EventHub.removeListener('Trigger event', this.state.isTriggered, () => {
            console..log('Listener is removed')
        })
    }
    render() {
        return ( ... )
    }
}

我们使用了 EventHub,很明显这不是组件里的东西,是属性外面的,所以这也是个 Effect。
而这次使用 useEffect 就不太一样了,因为我们是要擦屁股的,在组件 unmount 的时候取消监听。而 useEffect 提供了一个很好理解的用法。

function MyButton(props) {
    useEffect(() => {
        EventHub.on('Trigger event', this.state.isTriggered, () => {
            console.log('Do something')
        })

        return function cleanup() {
            EventHub.removeListener('Trigger event', this.state.isTriggered, () => {
                console.log('listener is removed')
            })
        }
    })
    return (...)
}

当组件 unmount 的时候就会去执行 cleanup 这个函数,是不是感觉一目了然?这里的函数名不是必须的,只是为了说明这里用来清理而已。

注意

  1. 代码简洁就不用说太多了,一看就知道谁好用了
  2. 而因为使用生命钩子是同步的,会阻塞页面的渲染,或者组件的销毁也会造成页面卡顿。而 setEffect 里面其实是异步进行的不会有这些影响
  3. 当然 setEffect 也不是全都是好的,比如每次 render 后都会用一个一模一样的函数去替换原来来的回调,以保持状态都是最新的

当然这只是简单的用法,更多的用法请看官方文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351