1.hook 是什么
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。
2.为什么要使用 hook(使用 hook的好处)
- 提供涉及state和生命周期钩子逻辑的抽象和复用,解决了过去因为和组件耦合导致的state和钩子的复用问题
- 将props的链式数据传导转化为横切式的传导方式,减少了props的传导层级,简化了结构
- 使函数组件也能操作state,完善了函数式组件的功能,避免了因为逻辑的复杂化时要把函数组件改成class组件的麻烦
- 和render-props/高阶组件实现方案的对比:比起别扭的render-props来,Hook的代码结构清晰明了。而相比起高阶组件,hook有内置的API,使用起来更简单方便
3.useState:让函数式组件拥有状态(异步更新)
class 示例:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button>
</div>
);
}
}
// 计数器
import { useState } from 'react'
const Test = () => {
const [count, setCount] = useState(0);
return (
<>
<h1>点击了{count}次</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
</>
);
}
export default Test
在 useState()中,它接受状态的初始值作为参数,即上例中计数的初始值,它返回一个数组,其中数组第一项为一个变量,指向状态的当前值。类似 this.state,第二项是一个函数,用来更新状态,类似 setState。
上述例子中没有 class 继承、没有 this、没有生命周期、代码更加简洁、这就是使用 hooks 的意义;
PS:class组件中this.setState更新是state是合并, useState中setState是替换。例如:
// 错误示例
import { useState } from 'react'
const Test = () => {
const [counts, setCounts] = useState({
num1: 0,
num2: 0
});
return (
<>
<h1>num1:{counts.num1}</h1>
<h1>num2:{counts.num2}</h1>
{/* //错误写法 可以看到useState中setState是替换,不会合并
<button onClick={() => setCounts({ num1: counts.num1 + 1})}>num1+1</button>
<button onClick={() => setCounts({ num2: counts.num2 + 1})}>num2+1</button> */}
<button onClick={() => setCounts({ ...counts, num1: counts.num1 + 1})}>num1+1</button>
<button onClick={() => setCounts({ ...counts, num2: counts.num2 + 1})}>num2+1</button>
</>
);
}
export default Test
PS:useState的异步更新
const [list, setlist] = useState([0]);
function change(){
setlist([22, 1])
console.log(list) //我想这里直接获得[22,1],但是拿到的是[0]
}
useEffect(() => {
console.log(list)
}, [list]
useState每次执行会返回一个新的state(简单类型的等值拷贝)
setState会触发UI更新(重新render,执行函数组件)
由于UI更新是异步任务,所以setState也是一个异步过程
解决方法(再写一个副作用Hook,只用于监视list值的变化,从而进行操作)
4.useEffect:副作用,取代生命周期
Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
React中的副作用操作:
1.发ajax请求数据获取
2.设置订阅 / 启动定时器
3.手动更改真实DOM
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
useEffect的基本用法:
1.默认是状态中任何数据发生变化副作用都会执行,父组件重新render 也会触发执行
2.useEffect第二个参数传入需要绑定的状态,可绑定多个;如果指定的是[], 回调函数只会在第一次render()后执行
3.清理副作用,return一个函数,作用相当于卸载钩子函数componentWillUnmount()
4.可以把 useEffect Hook 看做如下三个函数的组合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
import { useState, useEffect } from 'react'
const Test = () => {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
console.log('useEffect触发了---')
});
// 语法:useEffect(回调函数,[依赖值])
useEffect(() => {
console.log('useEffect触发了')
}, [count1]);
return (
<>
<h1>count1:{count1}</h1>
<h1>count2:{count2}</h1>
<button onClick={() => setCount1(count1 + 1)}>count1+1</button>
<button onClick={() => setCount2(count2 + 1)}>count2+1</button>
</>
);
}
export default Test
清理副作用:
在上面的操作中都不用清理的副作用,然而,有些副作用是需要去清理的,不清理会造成异常甚至内存泄漏,比如开启定时器,如果不清理,则会多次开启,从上面可以看到useEffect的第一个参数是一个回调函数,可以在回调函数中再返回一个函数,该函数可以在状态更新后第一个回调函数执行之前调用,具体实现:
useEffect(() => {
// 设置副作用
return () => { // 在组件卸载前执行
// 清理副作用 // 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
});
6.useRef:功能与React.createRef()一样
Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
// 用法:例如要实现点击button按钮使input输入框获得焦点:
import React, { useRef } from 'react';
const Test = () => {
const inputEl = useRef();
return (
<>
<input ref={inputEl} />
<button onClick={() => inputEl.current.focus()}>focus</button>
</>
);
}
export default Test