React Hooks 使用 | 青训营笔记

102 阅读3分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 6 天

本堂课重点内容

  1. 代码介绍
  2. React 的实现基本概要
  3. React 的实现 - Problem 1 & 2
  4. React 的实现 - How to Diff

详细知识点介绍

简单来说,hooks 就是那些在React中以use开头的函数,比较常见的便是 useStateuseEffect 以及一系列变种。 其中 useState 是React实现响应式的关键,这个函数将生成一个响应式变量(状态)

import React, { useState } from 'react';

function Example() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>you clicked {count} times</p>
            <button onclick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>
    )
}

上面的例子在React中被称为函数式组件,其返回值是jsx语法

实际上 jsx 是 React.createElement 方法的语法糖,且React 并不强制使用jsx

我们可以发现 useState 方法返回一个数组,其中数组的第一个元素便是响应式变量本身,第二个元素是该变量的 setter,即示例代码中的 useCount,这里使用es6解构赋值语法将useState返回的数组元素分别赋值给 countsetCount

由于count变量实际上被React管理,且count被改变时React需要去更新与count相关联的副作用/DOM树,所以我们不能直接对count进行赋值,而需要使用React为我们提供的setter方法去改变count的值。

注:

在 Vue 中也有相似的概念,即ref方法、reactive方法和$ref语法糖,而vue不需要使用setter方法去赋值的原因是两者的实现方式不同。Vue3使用Proxy对响应式变量进行了改造(可以想象成其他语言中的操作符重载),在赋值的时候自动通知vue的相关函数更新副作用和DOM。而$ref只是在编译阶段将其对应变量展开成xxx.value的形式,与ref一致。

在上面的示例代码中,我们为button增加了click事件的侦听器,当按钮被点击时,使得count加1。前面的p标签会随着count变量的变化而自动改变其内容。

useEffect 被称为副作用函数,用法比较丰富。有两个参数,其中第二个参数可以被省略。

useEffect(() => {
    console.log("hello")
})

上面这段代码中的 console.log 函数会在每次组件重新渲染时执行。为了防止其重复执行,我们可以使用useEffect的第二个参数,被称为useEffect的依赖项,简而言之,在给定依赖项时,副作用函数只有在依赖项改变时才会执行

const [count, setCount] = useState(0);

useEffect(() => {
    console.log(count)
}, [count])

例如上面这段代码,当 count 改变时,会将最新的 count 输出。

当依赖项置空,就可以模拟 componentDidMount,在副作用函数只在组件挂载时执行一次,其返回值会在组件卸载时执行,可以用来做一些计时器的清理(或者事件侦听器的移除)

useEffect(() => {
    const dosomething = () => { /* 做些什么 */ }
    button.addEventListener("click", dosomething)
    return () => removeEventListener("click", dosomething);
}, []);

注意

使用 useEffect 时经常忽略的一点就是,useEffect获取到的是定义时的state

参考下面的代码

const [count, setCount] = useState(0);

useEffect(() => {
    const id = setInterval(() => {
        setCount(count + 1);
    }, 500);
    return () => clearInterval(id);
}, []);

乍一看你可能会认为这里的count每隔半秒便会+1,但实际上,这里的count自从半秒之后一直都是1,因为useEffect获取的是定义时的state,即0,所以一直在执行0+1

而这种情况下,又不能将count作为依赖项,因为这样会导致计时器在count改变时被重置。

React给出的解决方案是下面这样的

const [count, setCount] = useState(0);

useEffect(() => {
    const id = setInterval(() => {
        setCount(count => count + 1);
    }, 500);
    return () => clearInterval(id);
}, []);

setter可以传入一个函数,该函数的第一个参数是最新的count