react自定义Hook 写法、规则(只能在组件/自定义Hook内调用)

0 阅读2分钟

React 自定义 Hook(Custom Hook)本质上是:把组件中可复用的状态逻辑抽出来。你做过 React,应该见过 useStateuseEffect,自定义 Hook 就是在这些基础上封装逻辑。


1. 自定义 Hook 写法

命名必须以 use 开头:

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

使用:

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 函数组件里调用

✅ 正确:

function User() {
    const [name] = useState('Tom')

    return <div>{name}</div>
}

❌ 错误:

普通函数:

function getData() {
    const [name] = useState('Tom')
}

报错:

Invalid hook call

规则2:只能在自定义 Hook 中调用 Hook

✅ 正确:

function useUser() {
    const [user] = useState(null)

    return user
}

因为:

自定义Hook
↓
内部可以继续调用 useState/useEffect

总结成一句:

Hook 只能在:

  • React 函数组件
  • 自定义 Hook(useXXX)

里面调用。


3. Hook 不能写在条件里

这是很多人踩坑。

错误:

function Demo({ show }) {

    if (show) {
        const [count] = useState(0)
    }

    return <div>demo</div>
}

原因:

React 要保证:

第一次渲染:
useState
useEffect

第二次渲染:
useState
useEffect

调用顺序必须一致。

条件会变成:

第一次:
useState

第二次:

React 不知道状态对应谁。


正确:

function Demo({ show }) {

    const [count] = useState(0)

    if (show) {
        console.log(count)
    }

    return <div></div>
}

4. Hook 不能写循环

错误:

for(let i=0;i<5;i++){
    useState()
}

错误:

list.map(() => {
   useEffect()
})

都会破坏调用顺序。


5. Hook 不能写嵌套函数

错误:

function App() {

   function handleClick() {
       useState()
   }

}

因为点击时才执行。


6. 自定义 Hook 常见封装

封装请求

以前组件:

useEffect(()=>{
   fetchUser()
},[])

抽:

function useUser() {
    const [user,setUser] = useState(null)

    useEffect(()=>{
        api.getUser()
          .then(res=>setUser(res))
    },[])

    return user
}

组件:

function App() {
    const user = useUser()

    return <div>{user?.name}</div>
}

类似 Vue Composition API:

useUser()

封装窗口监听

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
}

使用:

const width = useWindowSize()

7. React 为什么限制 Hook 只能顶层调用?

因为 React 内部靠调用顺序保存状态:

第一次:

1 -> useState(count)
2 -> useEffect()
3 -> useState(name)

第二次必须还是:

1 -> useState(count)
2 -> useEffect()
3 -> useState(name)

顺序变了:

count
name
effect

状态就乱了。

所以才有:

不能条件调用
不能循环调用
不能嵌套调用

8. 面试常问总结(建议背)

什么是自定义 Hook?

把组件内可复用的状态逻辑抽离成函数,以 use 开头,可复用 state、副作用、事件等逻辑。

Hook 使用规则?

  1. 只能在函数组件调用
  2. 只能在自定义 Hook 调用
  3. 不能写条件
  4. 不能写循环
  5. 不能写嵌套函数
  6. 必须顶层调用

为什么?

React 依赖 Hook 调用顺序保存状态,顺序变化会导致状态错乱。

这类问题在 React 中高级岗位很常见。