hook是什么呢?又改变了什么呢?我们先来看官网的原话:Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
为什么要使用hook呢?因为在react项目越来越大之后,我们会发现有时候一个class类往往很大,代码的复用性也很差,冗余的代码也很多,特别是在class类很多的时候,每个class类的生命周期的逻辑代码就很多了,维护起来更是麻烦。
跟着文章往下走,你就会知道hook使用的好处以及便捷,也能理解为什么react会推出hook这个功能。
首先我们来看一个最基本的例子,点击按钮,改变count的值
import React, {
Component } from 'react'
export default class App extends Component {
constructor() {
super()
this.state = {
count: 1
}
}
handleChangeCount = () => {
this.setState({
count: 2
})
}
render() {
return (
<div>
<div>{
this.state.count}</div>
<button onClick={
this.handleChangeCount}>修改count的值</button>
</div>
)
}
}
使用hook的useState写的话
import React, {
useState } from 'react'
export default function App() {
const [count, setCount] = useState(1) // 1表示count的初始值,setCount是改变count的方法
return (
<div>
<div>{
count}</div>
<button onClick={
() => {
setCount(2) }}>改变count的值</button>
</div>
)
}
我们会发现,代码量变成了原来的一半,而且看起来更好维护了。
我们来讲一讲useState,不使用useState的时候,我们会发现一个最麻烦的问题,就是要去维护this,因为我们知道,每一个class类的this,都是要从父组件继承的,本身是没有this的,所以需要使用constructor去初始化this。
而useState,让我们使用函数编程,我们之前写无状态组件的时候,用的就是函数编程,而使用useState之后,我们可以在函数编程里面使用状态。也就是说:count变成了函数里面的变量,setCount变成了函数里面的函数变量,那么使用的时候直接使用就行了,不再需要使用this
useState接收一个参数,返回的是一个数组,参数表示初始值,数组的第0项就是定义的变量,第1项就是改变变量的方法。
参数可以是基本数据类型,如string、number,也可以是复合数据类型,如array,object等,如下代码。
import React, {
useState } from 'react'
export default function App() {
const [count, setCount] = useState(0)
const [obj, setObj] = useState({
name: "aaa"})
const [arr, setArr] = useState([1,2,3])
return (
<div>
<div>{
count}</div>
<button onClick={
() => {
setCount(count + 1) }}>add</button>
<div>{
obj.name}---{
obj.age}</div>
<button onClick={
() => {
setObj({
...obj,
age: 20,
name: "bbb"
}) }}>add</button>
{
arr.map(item => {
return <div>{
item}</div>
})
}
<button onClick={
() => {
setArr([...arr, 4]) }}>add</button>
</div>
)
}
基本数据类型,可以直接修改,而复合数据类型,需要先通过解构运算符,然后再设置成新的复合数据类型。
看完了useState,我们再来看看useEffect,首先一样来看下官网是怎么说的:Effect Hook 可以让你在函数组件中执行副作用操作
什么是副作用呢?官网也都举了例子,比如调用ajax获取数据,订阅监听,启动定时器等。
简单来说,useEffect就是来替代我们日常使用的生命周期函数componentDidUpdate和componentDidMount的。
也就是说,使用了useEffect之后,我们就不再需要写类似于componentDidMount之类的生命周期函数了,也不需要每一次都要关系组件的生命周期执行到哪了,什么时候需要执行的。来看个例子
import React, {
Component } from 'react'
export default class App extends Component {
constructor() {
super()
this.state = {
count: 1
}
}
handleChangeCount = () => {
this.setState({
count: 2
})
}
componentDidMount = () => {
document.title = this.state.count
}
componentDidUpdate = () => {
document.title = this.state.count
}
render() {
return (
<div>
<div>{
this.state.count}</div>
<button onClick={
this.handleChangeCount}>修改count的值</button>
</div>
)
}
}
也就是,组件加载完成的时候,把title变成count的值,然后每次更新的时候,就更新title的count值。
再看看用useEffect写的
import React, {
useEffect, useState } from 'react'
export default function App() {
const [count, setCount] = useState(1)
useEffect(() => {
document.title = count
})
return (
<div>
<div>{
count}</div>
<button onClick={
() => {
setCount(2) }}>修改count的值</button>
</div>
)
}
可以看到,使用useEffect的代码,只有原来的一半,而且看起来也很舒服。
useEffect接收一个函数作为参数,会在页面加载完成的时候以及数据更新的时候,触发执行。
那么问题来了,如果我们只是想监听某些数据的更新,而不是全部数据的更新呢?useEffect还提供第二个参数,它是数组,数组的值表示想要监听变化的值,如
import React, {
useEffect, useState } from 'react'
export default function App() {
const [count, setCount] = useState(1)
useEffect(() => {
document.title = count
},[])
return (
<div>
<div>{
count}</div>
<button onClick={
() => {
setCount(2) }}>修改count的值</button>
</div>
)
}
此时我们传递了第二个参数为一个空数组,它表示不监听任何数值,那么useEffect就只会在页面加载完成的时候触发一次,而count更新的时候不会触发。如果想要count更新的时候触发,可以改成
import React, {
useEffect, useState } from 'react'
export default function App() {
const [count, setCount] = useState(1)
useEffect(() => {
document.title = count
},[count])
return (
<div>
<div>{
count}</div>
<button onClick={
() => {
setCount(2) }}>修改count的值</button>
</div>
)
}
我们再来想一个问题,我们都知道定时器在页面销毁的时候,需要清除,我们一般都会在componentUnMount,页面销毁的时候去把定时器清除,那么使用useEffect是如何清除呢?其实很简单,只需要在第一个参数的函数里面,return一个函数,函数里面写清除的方法就行了。来看代码
import React, {
useEffect, useState } from 'react'
export default function App() {
const [count, setCount] = useState(1)
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1)
}, 1000)
return () => {
clearInterval(timer)
}
})
return (
<div>
<div>{
count}</div>
</div>
)
}
这样在页面被卸载的时候,就会把定时器清除