Hooks是React 16.8
中的新增功能。它们允许您在不编写类class
的情况下使用状态state
和其他React
功能。
要使用Hooks请先升级你的React
包,包括ReactDom
。
为什么使用Hooks
我们总是会遇到某些困惑,比如:
- 当我们定义的一个组件只用到一两个
state
状态变量 - 一个组件只用到用到了生命周期某一个方法例如
componentDidMount
、componentWillUpdate
、componentWillMount
为了解决上面的问题,我们不得不定义一个class component
,来达到我们的目的,看起来代码有很多的冗余。而hooks
就是帮我们解决这些问题,让我们可以抛弃class component
拥抱function component
,使代码看起来清晰,整洁。
如何使用useState
引用官方的一个例子:
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
看一下上面做了什么操作:
- 定义了一个名称为
Example
的function component
- 使用
useState
在这个方法内部定义了一个变量count
- 并暴露了一个修改
count
的方法setCount
- 每次点击按钮对
count
递增
如果我们用class component
是如何实现上面的步骤的呢?等效于:
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>
);
}
}
一对比我们会发现使用Hooks
能让代码看起来更加有条理,简洁,也能使状态和UI层分离
当然Hooks
能让我们可以定义多个状态,每个状态都是独立的
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
同时我们会想到如果有很多个状态,我们要写多个useState
,Hooks
支持状态值是对象,我们也可以这样写:
const [data, setData] = useState({age: 42, fruit: 'banana', todos: [{ text: 'Learn Hooks' }]});
需要注意的是,上面的方括号[data, setData]
并不是Hooks的语法,而是JavaScript
的数组结构
如何使用useEffect
我们常常需要在组件加载的时候就需要运行某些函数,异步请求数据,我们可能会这样写:
import { Component } from 'react';
import axios from 'axios'
class Exampel extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
getItem = () => {
axios
.post('请求url')
.then(res => {
this.setState{
data: res.data
}
})
.catch(err => console.error(err))
}
componentDidMount () {
this.getItem()
}
//...
}
使用Hooks
的useEffect
我们可以这样写:
import { useState, useEffect } from 'react'
import axios from 'axios'
function Example(){
const [data, setData] = useState([]);
function getItem () {
axios
.post('请求url')
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
}
useEffect(() => {
getItem()
}, [])
//...
}
当然我开始也是这样写的,毫无疑问这样写目前看起来没有什么问题,能达到预期的效果。
但是有时候我会发现使用useEffect
的时候,里面的函数被无限循环调用了,这当然和我想象的初始化的时候只执行一次出入很大,这是为什么呢?
我们来看个例子:
在发送请求的时候,往往我们会添加一些参数:
import { useState, useEffect } from 'react'
import axios from 'axios'
function Example(){
const [data, setData] = useState([]);
const [params, setParams] = useState({});
function getItem () {
axios
.post('请求url', params) //注意我在这里使用了另一个状态params
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
}
useEffect(() => {
getItem()
}, [getItem]) //在参数改变的时候调用
//...
}
上面的例子,我在useEffect
里的依赖函数getItem
又对返回函数data
赋值,使得每次data
改变的时候useEffect
就会被触发,导致了无限循环。
解决方法就是把useEffect
的第二个参数改成[]
也就是useEffect(() => {....}, [])
,这意味着效果函数应该被调用一次:仅在第一次装载/渲染之后。但是这并不符合Hooks规范
。或者是你要的效果就是params改变后再次调用请求,用useCallback
看看能否解决问题。
import { useState, useEffect, useCallback } from 'react'
import axios from 'axios'
function Example(){
const [data, setData] = useState([]);
const [params, setParams] = useState({});
const getItem = useCallback(() => { //使用useCallback解决依赖函数的不可控变量
axios
.post('请求url', params) //注意我在这里使用了另一个状态params
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
}, [params, setData])//在useCallback里检查变量的改变
useEffect(() => {
getItem()
}, [getItem]) //符合useEffect规范
//...
}
我们推荐使用上面这种方法解决避免useEffect无限循环
无限循环调用产生的原因主要有以下几种,你可以一个个排除看看:
- 是不是在依赖函数里重新设置了状态
- 是什么操作引起了父组件重新render,导致本身组件也被重新加载,引起无限循环
-
useEffect
是不是被放在了判断if
语句或者循环for
语句里面了
当然我们还这样使用:
useEffect(() => {
function getItem () {
axios
.post('https://www.easy-mock.com/mock/5b4eb12d56c59c6f56266215/api/order_list', params) // 注意我在这里使用了另一个状态params
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
}
getItem()
}, [params, setData])
将useEffect
所有依赖的函数放在useEffect
里面,让useEffect
变得自给自足,这样我们减少了很多不可控制的因素,方法里面所有的依赖项都是可见的可控的。
还有一些Hooks
提供的方法useRef
、useCallback
等好用的方法可以查看官网API https://reactjs.org/docs/hooks-reference.html
总结:使用Hooks的好处:
- 可以让我们放弃
class component
,拥抱function component
- 可以在
function component
使用状态且是独立的状态,可以独立维护,这和状态组件有着很大的区别 - 可以让我们一个个组件去做优化,不需要全部重构
- 使状态与
UI
分离 - 让我们的代码看起来更加有条理、清晰、简洁
tips: 简书上交流可能会看不到消息,如有问题,欢迎进群交流50063010
参考链接1:https://reactjs.org/docs/hooks-intro.html
参考链接2:https://overreacted.io/a-complete-guide-to-useeffect/