React 自定义 Hook(Custom Hook)本质上是:把组件中可复用的状态逻辑抽出来 。你做过 React,应该见过 useState、useEffect,自定义 Hook 就是在这些基础上封装逻辑。
1. 自定义 Hook 写法
命名必须以 use 开头:
javascript
import { useState } from 'react'
function useCount(initialValue = 0) {
const [count, setCount] = useState(initialValue)
const increment = () => {
setCount(count + 1)
}
const decrement = () => {
setCount(count - 1)
}
return {
count,
increment,
decrement
}
}
export default useCount
使用:
javascript
import useCount from './hooks/useCount'
function App() {
const { count, increment } = useCount(10)
return (
<>
<div>{count}</div>
<button onClick={increment}>
+
</button>
</>
)
}
结果:
点击按钮
↓
count变化
↓
组件重新渲染
类似把业务逻辑抽出去。
2. React Hook 两条核心规则(必须记)
官方规则可概括成:
规则1:只能在 React 函数组件里调用
✅ 正确:
javascript
function User() {
const [name] = useState('Tom')
return <div>{name}</div>
}
❌ 错误:
普通函数:
scss
function getData() {
const [name] = useState('Tom')
}
报错:
r
Invalid hook call
规则2:只能在自定义 Hook 中调用 Hook
✅ 正确:
csharp
function useUser() {
const [user] = useState(null)
return user
}
因为:
bash
自定义Hook
↓
内部可以继续调用 useState/useEffect
总结成一句:
Hook 只能在:
- React 函数组件
- 自定义 Hook(useXXX)
里面调用。
3. Hook 不能写在条件里
这是很多人踩坑。
错误:
javascript
function Demo({ show }) {
if (show) {
const [count] = useState(0)
}
return <div>demo</div>
}
原因:
React 要保证:
makefile
第一次渲染:
useState
useEffect
第二次渲染:
useState
useEffect
调用顺序必须一致。
条件会变成:
makefile
第一次:
useState
第二次:
无
React 不知道状态对应谁。
正确:
javascript
function Demo({ show }) {
const [count] = useState(0)
if (show) {
console.log(count)
}
return <div></div>
}
4. Hook 不能写循环
错误:
scss
for(let i=0;i<5;i++){
useState()
}
错误:
scss
list.map(() => {
useEffect()
})
都会破坏调用顺序。
5. Hook 不能写嵌套函数
错误:
scss
function App() {
function handleClick() {
useState()
}
}
因为点击时才执行。
6. 自定义 Hook 常见封装
封装请求
以前组件:
scss
useEffect(()=>{
fetchUser()
},[])
抽:
scss
function useUser() {
const [user,setUser] = useState(null)
useEffect(()=>{
api.getUser()
.then(res=>setUser(res))
},[])
return user
}
组件:
javascript
function App() {
const user = useUser()
return <div>{user?.name}</div>
}
类似 Vue Composition API:
scss
useUser()
封装窗口监听
javascript
function useWindowSize() {
const [size,setSize] =
useState(window.innerWidth)
useEffect(()=>{
const resize = ()=>{
setSize(window.innerWidth)
}
window.addEventListener(
'resize',
resize
)
return ()=>{
window.removeEventListener(
'resize',
resize
)
}
},[])
return size
}
使用:
scss
const width = useWindowSize()
7. React 为什么限制 Hook 只能顶层调用?
因为 React 内部靠调用顺序保存状态:
第一次:
scss
1 -> useState(count)
2 -> useEffect()
3 -> useState(name)
第二次必须还是:
scss
1 -> useState(count)
2 -> useEffect()
3 -> useState(name)
顺序变了:
count
name
effect
状态就乱了。
所以才有:
不能条件调用
不能循环调用
不能嵌套调用
8. 面试常问总结(建议背)
什么是自定义 Hook?
把组件内可复用的状态逻辑抽离成函数,以
use开头,可复用state、副作用、事件等逻辑。
Hook 使用规则?
- 只能在函数组件调用
- 只能在自定义 Hook 调用
- 不能写条件
- 不能写循环
- 不能写嵌套函数
- 必须顶层调用
为什么?
React 依赖 Hook 调用顺序保存状态,顺序变化会导致状态错乱。
这类问题在 React 中高级岗位很常见。