这是我参与「第五届青训营」伴学笔记创作活动的第 6 天
本堂课重点内容
- 代码介绍
- React 的实现基本概要
- React 的实现 - Problem 1 & 2
- React 的实现 - How to Diff
详细知识点介绍
简单来说,hooks 就是那些在React中以use开头的函数,比较常见的便是 useState 和 useEffect 以及一系列变种。
其中 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返回的数组元素分别赋值给 count 和 setCount。
由于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