1. React Hooks 是什么
React Hooks 是 React V16.8 版本新增的特性,即在不编写类组件的情况下使用 state 以及 React 的新特性。React 官网提供了 10 个 Hooks API,来满足我们在函数组件中定义状态,提供类似生命周期的功能和一些高级特性。
2. Hooks 的诞生背景
2.1. 类组件的不足
- 状态逻辑难以复用:
在旧版本的 React 中,想要实现逻辑的复用,需要使用到HOC
或者Render Props
,增加了组件的使用层级,同时学习使用成本也比较高。 - 使用趋于复杂且维护成本较高
有多个监听状态的生命周期,同一个功能的整个过程可能要在不同的生命周期完成,不够统一;尤其是引入 Redux 后,会变得复杂,维护成本较高。 - this 绑定问题
在类组件中如果不使用箭头函数,需要显示的绑定 this,容易造成 this 丢失,导致数据混乱。
2.2. Hooks 的优势
- 自定义 Hooks 可以实现公共的逻辑抽离,便于复用
- 可以将组件抽成更小的函数单元,实现一个函数只关注一个功能,更加清晰
- 更加丰富的性能优化手段
- 组件树层级变浅,使用 HOC/Render Props 实现组件的状态复用,会增加组件的层级,但 Hooks 无需增加层级即可实现。
3. 10 个官方 Hooks 案例详解
3.1. useState
import React, {
useState } from 'react';
import ReactDOM from 'react-dom';
import {
Button,Modal } from 'antd'
/**
* useState:定义组件的状态
* 作用:
* 通过传入 `useState` 参数后返回一个带有默认状态和改变状态函数的数组。通过传入新状态给函数来改变原本的状态值。
*/
// 类组件写法
class Example extends React.Component {
constructor() {
super()
this.state = {
count: 0}
}
render() {
return (
<div>
<div>你点击了{
this.state.count}次</div>
<button onClick={
() => this.setState({
count: this.state.count +1})}>点击</button>
</div>
)
}
}
// hooks 写法
function Example1() {
// 定义一个count变量,赋初始值0
const [count,setCount] = useState(0)
return (
<div>
<div>你点击了{
count}次</div>
<button onClick={
() => setCount(count +1 )}>点击</button>
</div>
)
}
// setCount 接收函数作为参数
function Example2() {
const [count,setCount] = useState(0)
// preCount 参数为上一次的值
const countAction = (preCount,a) => preCount + a
return (
<div>
<div>你点击了{
count}次</div>
<button onClick={
() => setCount(countAction(count,1))}>点击</button>
</div>
)
}
/**
* 2 . renderProps 和 hooks 的比较。彻底理解 hooks 的价值和优点。
*/
// renderProps 抽离公共逻辑
class Toggle extends React.Component {
// 定义默认属性
state= {
on: false}
constructor(props) {
super(props)
// 接收父组件传递的参数
this.state.on = this.props.initial
}
toggle = () => {
this.setState({
on: !this.state.on })
}
render() {
// 向子组件传递了属性和方法
return this.props.children(this.state.on,this.toggle)
}
}
function Example3() {
return (
<Toggle initial={
false}>
{
/* 通过一个方法接收参数 */}
{
(on,toggle) => (
<React.Fragment>
<Button type="primary" onClick={
toggle}>打开弹框</Button>
<Modal visible={
on} onOk={
toggle} onCancel={
toggle}>我是弹框</Modal>
</React.Fragment>
)
}
</Toggle>
)
}
// hooks 写法 - 优势:多个状态不会产生嵌套
function Example4 () {
const [visible,setVisible] = useState(false)
return (
<div>
<Button type='primary' onClick={
() => setVisible(true)}>打开弹框</Button>
<Modal visible={
visible} onOk={
() => setVisible(false)} onCancel={
() => setVisible(false)}>我是弹框内容</Modal>
</div>
)
}
const App = props => <div>
<Example />
<hr />
<Example1 />
<hr/>
<Example2/>
<hr />
<Example3 />
<hr />
<Example4 />
</div>
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
3.2. useEffect
import React, {
useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import {
Button,Modal } from 'antd'
/**
* useEffect: 处理副作用(副作用:指那些没有发生在数据向视图转换过程中的逻辑,如 ajax 请求、访问原生dom 元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志等。)
* 作用: 函数组件能保存状态,但是对于异步请求,副作用的操作还是无能为力,所以 React 提供了 useEffect 来帮助开发者处理函数组件的副作用,类似生命周期函数,相当于是 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合,可以通过传参及其他逻辑,分别模拟*这三个生命周期函数。
* useEffect具有以下5个特性:
* 1. 第一次渲染时执行,任何状态发生变化都执行 - 只指定一个回调函数作为参数, 相当于componentDidMount & componentDidUpdate
* 2. 第一次渲染执行,任何状态发生变化时不执行
* 3. 第一次渲染执行,通过第二个参数指定状态发生变化时执行,其他状态发生变化不执行
* 4. 监听多个状态时,可以同时定义多个useEffect
* 5. 组件卸载时会执行回调函数返回的回调函数 - 相当于componentWillUnmount
* 6. 未传递第二个参数,所有状态更新就执行useEffect,或者指定状态,对应状态更新执行useEffect时,会先执行返回值回调,再执行第一个回调参数(第二个参数为空数组时任何状态更新都不会执行)
*
/**
* 1. useEffect只有一个回调函数作为第一个参数时:
* 1.1.初始化时会执行一次回调函数
* 1.2.任一一个状态数据发生变化时都会执行回调函数
*/
function Example () {
const [count,setCount] = useState(0)
useEffect(() => {
// 初始化时执行一次,count每次变化的时候都会执行
console.log('我执行啦!')
})
return (
<div>
<div>点击了{
count}次</div>
<Button type='primary' onClick={
() => setCount(count+1)}>点击</Button>
</div>
)
}
/**
* 2. useEffect传入两个参数:第一个参数是回调函数,第二个参数是空数组:
* useEffect的回调函数只会在初始化渲染时执行一次
*/
function Example1() {
const [count,setCount] = useState(0)
useEffect(() => {
// 只会在初次渲染时执行,任何状态数据发生变化都不会执行
console.log('我执行啦111111!')
},[])
return (
<div>
<div>你点击了{
count}次</div>
<Button type='primary' onClick={
() => setCount(count + 1)}>点击</Button>
</div>
)
}
/**
* 3. useEffect 传入两个参数,第一个是回调函数,第二个是指定数据的数组
* 3.1 初次渲染时执行一次回调函数
* 3.2 指定数据发生变化时执行一次回调函数
*/
function Example2() {
const [visible,setVisible] = useState(false)
const [count,setCount] = useState(0)
useEffect(() => {
// 初始渲染时会执行一次,visible状态发生变化时会执行,count发生变化时则不会执行
console.log('我最帅了')
},[visible])
return (
<div>
<div>点击了{
count}次</div>
<Button type='primary' onClick={
() => setCount(count +1) <