生命周期

理解

生命周期是指一个组件从被创建至销毁会经历的一系列的过程,这个过程就叫生命周期

生命周期钩子函数是类组件独有的东西,自从react16.8推出hooks以来,整体就已经开始以函数组件为主了。

Hook从此告别类组件

Hook实际上就是JavaScript的函数,但是这些函数相较于其他普通的函数,有些不一样。在React中使用的时候要遵循以下规则

  • 只能在函数最外层调用Hook。不要在循环、条件判断、或者子函数中调用
  • 只能在React的函数组件中调用Hook,不要在其他JavaScript函数中调用

useState

使用useState可以创建组件的状态,使用解构进行接收,相当于之前在类组件里面的state

  1. 简单使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from "react";

export default function UseState() {
const [count, setCount] = React.useState(0);
const handleClick = () => {
setCount((count) => count + 1); // 1
setCount((count) => count + 1); // 2
console.log(count); // 0
};
return (
<div>
<p>count: {count}</p>
<button onClick={handleClick}>点击</button>
</div>
);
}

useEffect

概述

看完文档下来,Effect,看看插件给我们翻译的是啥意思

Effect

查文档,看视频,了解到这个hook用于解决函数式编程所产生的副作用。纯函数式编程规范规定一个函数对于相同的输入一定是有相同的输出的。比如下面的函数

1
2
3
4
5
function add(x,y){
return x + y;
}
add(1,2) //3
add(2,1) //3

而useEffect的出现就是为了解决函数不纯所产生的副作用,副作用又是什么?

如果在一个函数中,存在副作用,那么就称这个函数不是一个纯函数,指的是函数的结果是不可控的,不可预期的

常见的副作用:

常见的副作用有发送网络请求、添加一些监听的注册和取消注册,文件读取、手动修改DOM元素

之前是怎么解决这些副作用的?

在组件的生命周期里面去解决,比如组件渲染所需要的一些比要的数据,要通过发送网络请求去获取。可以在组件被挂载之前去发送网络请求。

一个例子

这个例子操作了在Effect里面操作的DOM元素,Effect在组件渲染之后调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { useEffect } from "react";

export default function Index() {
let [count, setCount] = React.useState(0);
useEffect(() => {
console.log(count);
document.title = `You clicked ${count} times`;
});
return (
<div>
<button onClick={() => setCount(++count)}>+1</button>
</div>
);
}

来看一下效果

effect01

当切换到这个组件的视图的时候,这个组件被渲染了,渲染完成了就调用了useEffect这个Hook。不妨再再观察一下,我发现如果这个组件没有触发重新渲染,useEffect是不会调用的。

总结

useEffect只会在组件渲染之后调用,重新渲染也会触发这个特性


但是有些时候我只想他在某个时刻,一些特定条件下面才触发useEffect。这就要介绍useEffect的第2个参数了。第二个参数是一个数组,里面设置触发的依赖项。比如这里设置了count1,也就是当count1变化的时候,才会触发useEffect这个钩子。这里给我的感觉有点像vue里面的watch。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { useEffect, useState } from "react";
export default function Index() {
let [count1, setCount1] = useState(0);
let [count2, setCount2] = useState(0);
let [count3, setCount3] = useState(0);
useEffect(() => {
console.log("useEffect执行了");
return () => {
console.log("####");
};
},[count1]);
return (
<div>
<div><span>{count1}</span> <button onClick={() => setCount1(count1+=1)}>+1</button></div>
<div><span>{count2}</span> <button onClick={() => setCount2(count2+=1)}>+1</button></div>
<div><span>{count3}</span> <button onClick={() => setCount3(count3+=1)}>+1</button></div>
</div>
);
}

先来看一下效果

Effect02

控制台输出的第一个Effect是组件初次渲染输出的,代码不是设置了依赖项count1嘛。点击增加count1的按钮,输出##

然后执行输出useEffect执行了

这个时候我在想,组件渲染、useEffect、return里面的函数。这个3个的执行顺序是怎么样的。其实也很好分析,useEffect是在组件渲染之后执行的,那不就能得出这仨玩意的执行顺序了嘛

effect03

先是组件渲染,然后是执行useEffect,最后执行return里面的东西 。最后的return里面的东西也叫清理函数。他就是用来清理useEffct所产生的副作用的,所以上面的gif的控制台输出##其实是对应的是清理组件第一次渲染。也就是说,这个清理函数会在下一次渲染之前执行。

自定义Hook

Hook就是特殊的js函数

主要区别是

  • 自定义Hook调用了诸如useState、useEffect、useRef等这些内置的Hook,由此可以通过内置的Hooks获得Fiber的访问方式,可以实现组件级别存储数据的方案等。
  • 约定自定义Hook以use开头