Hook 是 React 16.8 的新增特性,它可以让我们在不使用 class 的情况下使用 state 和 React 的其他特性。
# 知识回顾
在 react 中我们就已经提到了,函数式组件的缺点:
缺点 | 描述 |
---|---|
没有内部状态 | class 组件可以定义 state 来保存自己的内部状态,而函数式组件中函数每次调用会产生新的临时变量 |
没有生命周期 | class 组件可以在特定周期执行特殊的操作(如:发送网络请求),而函数式组件中函数每次调用都会重新发送网络请求 |
没有组件实例 | 意味着不会有 class 组件中的 this 指向问题 |
除此之外,我们在 React 核心中还接触到了:
hooks | 作用 |
---|---|
memo() | 和 PureComponents 功能类似 |
useRef() + forwardRef() | 获取 DOM 元素 |
# 1、useState
类数组实现
// 使用 PureComponent
class Demo extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
count: 1
}
}
countChange() {
this.setState({
count: this.state.count + 1
})
}
render() {
const { count } = this.state
return (
<div>
<p>计数:{count}</p>
<button onClick={(e) => this.countChange()}>count值 +1</button>
</div>
)
}
}
提示
hook 只能在函数组件内的顶层调用
hook 不能在类组件、普通函数等中调用
import { memo, useState } from 'react'
function Demo(props) {
// 数组解构
let [count, setCount] = useState(1)
function countChange() {
setCount(count + 1)
}
return (
<div>
<p>计数:{count}</p>
<button onClick={countChange}>count值 +1</button>
</div>
)
}
// 使用 memo
export default memo(Demo)
# 2、useEffect
Effect Hook 可以让我们完成一些在类组件中的生命周期中使用的功能(副作用操作):
网络请求、DOM 操作、事件监听等
提示
-一个函数式组件中是可以有多个 Effect 的,所以当 Effect 中内容过多时,我们可以将它们拆分出来
vue 实现
利用 vue-router 的导航守卫
// 当前组件中
router.afterEach((to, from) => {
document.title = to.meta.title || 'lencamo'
})
类组件实现
// 待跳转组件中
class Demo extends React.PureComponent {
componentDidMount() {
document.title = this.state.pageTitle
}
// componentDidUpdated() {
// document.title = this.state.pageTitle
// }
}
import { useState, useEffect } from 'react'
function Demo(props) {
let [pageTitle, setPageTitle] = useState('分类')
// 当前组件发生渲染时自动执行🚩
useEffect(() => {
//
document.title = pageTitle
})
return <div></div>
}
# 3、清除 Effect
其实,我们可以把 useEffect Hook 看做 componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个函数的组合。
在类组件中我们可以使用componentDidMount
和componentWillUnmount
来进行订阅和取消订阅,那在函数式组件中如果实现呢?
关于下面的 cleanup 函数名是方便阅读而命名的,可以不用命名返回一个函数就可以了
function Demo(props) {
useEffect(() => {
console.log('组件被创建了')
const unsubscribe = store.subscribe(() => console.log(store.getState())) // 启用
return function cleanup() {
unsubscribe() // 取消
console.log('组件被销毁了')
}
})
return <div></div>
}
# 4、Effect 性能优化
默认情况下,每当函数式组件重新渲染时(jsx 改变),useEffect 都会重新执行。
这一特性在我们进行事件监听时,好像还行;但如果我们进行网络请求、订阅操作时,显然是没有必要的,还会造成一些性能问题。那怎么解决能?
答案
前面我们仅仅使用了 useEffect() 的第一个参数,其实它还有而二个参数用来标明哪些操作需要重新执行。
需要注意的是,我们标明的是受该操作影响的 state
useEffect(function, array)
如果我们希望当前的 Effect 不受任何依赖影响,我们可以传入一个空数组
function Demo(props) {
useEffect(() => {
console.log('开启订阅')
console.log('发起网络请求')
return function cleanup() {
console.log('取消订阅')
}
}, [])
useEffect(() => {
console.log('发起网络请求')
console.log('修改count值')
}, [count])
return <div></div>
}