一、为什么要发明hooks?
1.在组件之间复用状态逻辑很难
在class组件中,我们如果复用状态逻辑,可以使用比如 render props 和 高阶组件。但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。而hooks可以简单的去实现状态逻辑的复用。
2.复杂组件变得难以理解
class组件的每个生命周期常常包含一些不相关的逻辑,完全不相关的代码却在同一个方法中组合在一起,如此很容易产生 bug,并且导致逻辑不一致。
3.难以理解的 class
class 是学习 React 的一大屏障,还必须去理解 JavaScript 中 this 的工作方式。还不能忘记绑定事件处理器。没有稳定的语法提案,这些代码非常冗余。
二、常用的hook及其用法
1.useState:让函数组件具有维持状态的能力
state(在一个函数组件的多次渲染之间,这个 state 是共享的) 是 React 组件的一个核心机制,useState 这个 Hook 就是用来管理 state 的,下面我分别用hooks组件和class组件来对比一下两种写法。
class组件:
import React from "react";
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 1
};
}
handleAdd = () => {
const newNum = this.state.num + 1;
this.setState({
num: newNum });
};
render() {
return (
<div>
{
this.state.num}
<button onClick={
this.handleAdd}>增加</button>
</div>
);
}
}
export default Demo;
hooks组件:
import React, {
useState } from "react";
const Demo = () => {
const [num, setNum] = useState(1);
const handleAdd = () => {
const newNum = num + 1;
setNum(newNum);
};
return (
<div>
{
num}
<button onClick={
handleAdd}>增加</button>
</div>
);
};
export default Demo;
useState(initialState) 的参数 initialState 是创建 state 的初始值;返回值是一个有着两个元素的数组,第一个元素用来读取 state 的值,第二个则是用来设置这个 state 的值;如果要创建多个 state,那么我们就需要多次调用 useState。
useState 应该算最简单的一个 Hooks,但在使用中,也有很多技巧可循,如果严格按照以下几点,代码可维护性直接翻倍:
重点:
1.能用其他状态计算出来就不用单独声明状态。一个 state 必须不能通过其它 state/props 直接计算出来,否则就不用定义 state。
2.保证数据源唯一。在项目中同一个数据,保证只存储在一个地方。不要既存在 redux 中,又在组件中定义了一个 state 存储;不要既存在父级组件中,又在当前组件中定义了一个 state 存储;不要既存在 url query 中,又在组件中定义了一个 state 存储。
3.useState 适当合并。useState 拆分过细,导致代码中一大片 useState,最终使得项目bug多多难以维护。
2.useEffect:执行副作用(副作用是指一段和当前执行结果无关的代码)
对应到 Class 组件,那么 useEffect 就涵盖了 ComponentDidMount、componentDidUpdate 和 componentWillUnmount 三个生命周期方法。
useEffect接收两个参数,第一个为要执行的函数 callback,第二个是可选的依赖项数组 dependencies,其中依赖项是可选的,如果不指定,那么 callback 就会在每次函数组件执行完后都执行;如果指定了,那么只有依赖项中的值发生变化的时候,它才会执行。
1.有依赖项时,只有当依赖项发生变化时才执行。例如:
const [state, setState] = useState(0);
useEffect(() => {
// state改变之后才会执行
console.log('state-changed');
}, [state]);
2.没有依赖项,则每次 render 后都会重新执行。例如:
useEffect(() => {
// 每次 render 完一定执行
console.log('re-rendered');
});
3.空数组作为依赖项,则只在首次执行时触发,对应到 Class 组件就是 componentDidMount。例如:
useEffect(() => {
// 组件首次渲染时执行,等价于 class 组件中的 componentDidMount
console.log('did mount');
}, [])
4.useEffect 还允许你返回一个函数,用于在组件销毁的时候做一些清理的操作,类似于componentWillUnmount,例如:
useEffect(() => {
return () => {
// 组件卸载时执行,等价于 class 组件中的 componentWillUnmount
clearInterval(timer1);
}
}, [