React Hooks 入门
- 作者:Bougie
- 创建于:2019-09-15
- 更新于:2023-03-09
# 常用 hook
# useState
建立一个 state,改变 state 会触发组件重新渲染
import React, { useState } from 'react'
function Example() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
setCount
可传入函数,回调值为当前 state
setCount((prev) => {
console.log(prev)
return prev + 1
})
某些闭包情况下可能获取不到准确的 state 值,例如
import React, { useState, useEffect, useRef } from 'react'
// 计时到 30s 停止
function Example() {
const timerRef = useRef()
const [count, setCount] = useState(0)
useEffect(() => {
timerRef.current = setInterval(() => {
if (count === 30) {
clearInterval(timerRef.current)
}
setCount(count + 1)
}, 1000)
})
return <div>{count} seconds.</div>
}
由于 setInterval 形成了闭包,setInterval 回调函数里面的两个 count 值永远为 0。此时 setCount 应使用回调函数写法
import React, { useState, useEffect, useRef } from 'react'
// 计时到 30s 停止
function Example() {
const timerRef = useRef()
const [count, setCount] = useState(0)
useEffect(() => {
timerRef.current = setInterval(() => {
setCount((prev) => {
if (count === 30) {
clearInterval(timerRef.current)
return prev
}
return prev + 1
})
}, 1000)
})
return <div>{count} seconds.</div>
}
# useEffect
Effect Hook 可以让你在函数组件中执行副作用操作。此时已进入 render commit 阶段,可以获取组件 Dom.
- 什么是副作用操作?
先要从纯函数说起,纯函数的特性:相同的输入,永远得到相同的输出。受外界影响的、甚至改变外界状态的函数被称为是有副作用的函数。我们常常在 useEffect 里面获取 api 数据,操作 dom,因此说 useEffect 是有副作用的。
可以把 useEffect
Hook 看做 componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个函数的组合
- 模拟
componentDidMount
安装 eslint-plugin-react-hooks
,会自动添加 hooks 依赖项。若想避免依赖项被添加到 useEffect
中,可以用 useCallback
包裹回调函数。
function Example({ dispatch, state }) {
const fetchData = useCallback(() => {
axios.get('http://xxx').then((data) => {
dispatch({
type: 'ACTION_NAME',
payload: data
})
})
}, [dispatch])
useEffect(fetchData, [])
return <div>{JSON.stringfy(state)}</div>
}
- 模拟
componentDidUpdate
还是使用 useCallback
包裹回调函数,useEffect
依赖项视需求添加。相当于 componentDidUpdate
的 if 判断
- 模拟
componentWillUnmount
useEffect
的回调函数的返回函数会在组件即将取消挂载的时候执行
import React, { useState, useEffect, useRef } from 'react'
// 计时到 30s 停止
function Example() {
const timerRef = useRef()
const [count, setCount] = useState(0)
useEffect(() => {
timerRef.current = setInterval(() => {
setCount((prev) => {
if (count === 30) {
clearInterval(timerRef.current)
return prev
}
return prev + 1
})
}, 1000)
return () => {
clearInterval(timerRef.current)
}
})
return <div>{count} seconds.</div>
}
# useMemo
计算并缓存一个值,依赖项改变时重新计算
function Example({ a, b }) {
const computedValue = useMemo(() => a + b, [a, b])
return <div>{computedValue}</div>
}
不要试图用 useMemo
创建一个永远不会变化的值,这会引起一些问题
function Example({ a, b }) {
const computedValue = useMemo(() => a + b, []) // 不要传空依赖项
return <div>{computedValue}</div>
}
# useCallback
缓存一个方法,useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
# useImperativeHandle
给函数式组件添加实例方法,与 forwardRef
组合使用
import React, { useImperativeHandle, forwardRef } from 'react'
function Form(props, ref) {
useImperativeHandle(ref, () => ({
validate: () => {
// ...
},
resetValidate: () => {
// ...
}
}))
// ...
}
export default forwardRef(Form)