☰
对比react钩子函数
2026/10/11 7:01:18 网站建设 项目流程

有 Vue 基础,学 React 钩子会快很多。核心差别是:

Vue 靠「响应式系统自动追踪依赖」,React 靠「钩子函数手动声明副作用和缓存」。

下面用 Vue 类比,把常用钩子一次讲清。


一、先建立心智模型

Vue React
ref / reactive useState
computed useMemo
watch / watchEffect useEffect
onMounted / onUnmounted useEffect(靠依赖数组控制)
provide / inject useContext
模板 ref useRef
Pinia / Vuex useReducer + useContext(轻量场景)
自定义 Hook(Vue 3 useXxx) 自定义 Hook(useXxx)

关键区别:

· Vue:数据变了,依赖自动重新计算。
· React:数据变了,整个函数组件重新执行,你需要用钩子告诉 React「什么该缓存、什么该执行」。


二、useState —— 相当于 ref / reactive

作用

声明一个响应式状态,改变它会触发组件重新渲染。

Vue 写法

<script setup> import { ref } from 'vue'; const count = ref(0); function add() { count.value++; } </script> <template> <button @click="add">{{ count }}</button> </template>

React 写法

import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); function add() { setCount(count + 1); } return <button onClick={add}>{count}</button>; }

对比

Vue React
const count = ref(0) const [count, setCount] = useState(0)
count.value count
count.value++ setCount(count + 1)
自动追踪依赖 手动调用 setXxx 触发更新

使用场景

· 计数器、表单输入、开关状态、列表数据
· 任何需要「变了就刷新页面」的数据

注意

// ❌ 不能直接改 count++; // ✅ 用 set 函数 setCount(count + 1); // ✅ 依赖旧值时用函数式 setCount(prev => prev + 1);

三、useEffect —— 相当于 watch + 生命周期

这是 React 最重要的钩子,也是 Vue 转 React 最容易懵的。

作用

处理副作用:请求数据、订阅事件、操作 DOM、定时器。

三种形态

// 1. 每次渲染都执行 useEffect(() => { console.log('渲染了'); }); // 2. 只在挂载时执行一次(相当于 onMounted) useEffect(() => { console.log('挂载'); }, []); // 3. 依赖变化时执行(相当于 watch) useEffect(() => { console.log('count 变了', count); }, [count]);

清理函数(相当于 onUnmounted)

useEffect(() => { const timer = setInterval(() => { console.log('tick'); }, 1000); // 组件卸载或依赖变化前执行 return () => { clearInterval(timer); }; }, []);

Vue 对比

<script setup> import { watch, onMounted, onUnmounted } from 'vue'; onMounted(() => { console.log('挂载'); }); watch(count, (newVal) => { console.log('count 变了', newVal); }); onUnmounted(() => { console.log('卸载'); }); </script>
useEffect(() => { console.log('挂载'); return () => console.log('卸载'); }, []); useEffect(() => { console.log('count 变了', count); }, [count]);

使用场景

场景 写法
请求接口 useEffect(() => { fetchData() }, [])
监听窗口大小 useEffect(() => { window.addEventListener(…) }, [])
定时器 useEffect(() => { const t = setInterval(…); return () => clearInterval(t) }, [])
依赖某个值变化 useEffect(() => {…}, [keyword])
操作 DOM useEffect(() => { ref.current.focus() }, [])

依赖数组规则

useEffect(() => {...}); // 每次渲染都跑 useEffect(() => {...}, []); // 只跑一次 useEffect(() => {...}, [a]); // a 变了就跑 useEffect(() => {...}, [a,b]); // a 或 b 变了就跑

记住:依赖数组里用到的外部变量都要写进去,否则拿到旧值。


四、useMemo —— 相当于 computed

作用

缓存计算结果,依赖不变就不重新计算。

Vue 写法

<script setup> import { ref, computed } from 'vue'; const list = ref([1, 2, 3]); const total = computed(() => { console.log('计算了'); return list.value.reduce((a, b) => a + b, 0); }); </script>

React 写法

import { useState, useMemo } from 'react'; function App() { const [list] = useState([1, 2, 3]); const total = useMemo(() => { console.log('计算了'); return list.reduce((a, b) => a + b, 0); }, [list]); return <div>{total}</div>; }

对比

Vue React
computed(() => …) useMemo(() => …, [deps])
自动追踪依赖 手动写依赖数组
值缓存 值缓存

使用场景

· 复杂计算(排序、过滤、统计)
· 避免每次渲染都重新算
· 作为其他 Hook 的依赖时保持引用稳定

注意

// 简单计算不用 useMemo,反而增加开销 const double = count * 2; // 直接算就行 // 复杂计算才用 const sorted = useMemo(() => bigList.sort(...), [bigList]);

五、useCallback —— 缓存函数

作用

缓存函数引用,依赖不变就返回同一个函数。

为什么需要?

React 每次渲染都会重新创建函数,如果这个函数传给子组件,子组件会认为 props 变了而重新渲染。

写法

import { useState, useCallback } from 'react'; function App() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('点击'); }, []); return <Child onClick={handleClick} />; }

Vue 对比

Vue 里没有直接对应,因为 Vue 的响应式系统不需要你手动缓存函数。

使用场景

· 函数作为 props 传给 React.memo 包裹的子组件
· 函数作为其他 Hook 的依赖

注意

// 大部分情况不需要 useCallback // 只有性能问题出现时才用

useMemo 缓存值,useCallback 缓存函数。

// 等价 useCallback(fn, deps) === useMemo(() => fn, deps)

六、useRef —— 相当于模板 ref + 普通变量

作用

  1. 获取 DOM 元素(像 Vue 的模板 ref)

  2. 保存不触发渲染的变量

  3. 获取 DOM

Vue:

<template> <input ref="inputRef" /> </template> <script setup> import { ref, onMounted } from 'vue'; const inputRef = ref(null); onMounted(() => { inputRef.value.focus(); }); </script>

React:

import { useRef, useEffect } from 'react'; function App() { const inputRef = useRef<HTMLInputElement>(null); useEffect(() => { inputRef.current?.focus(); }, []); return <input ref={inputRef} />; }
  1. 保存变量(改了不重新渲染)
function App() { const countRef = useRef(0); function handleClick() { countRef.current++; console.log(countRef.current); // 变了,但页面不刷新 } return <button onClick={handleClick}>点击</button>; }

对比

Vue React
const el = ref(null) + 模板 ref=“el” const el = useRef(null) + ref={el}
el.value el.current
let timer 普通变量 useRef 保持跨渲染

使用场景

· 操作 DOM:聚焦、滚动、播放视频
· 保存定时器 ID
· 保存上一次的值
· 保存不需要触发渲染的数据


七、useContext —— 相当于 provide / inject

作用

跨层级传数据,不用一层层 props 传。

Vue 写法

<!-- 父 --> <script setup> import { provide } from 'vue'; provide('theme', 'dark'); </script> <!-- 深层子 --> <script setup> import { inject } from 'vue'; const theme = inject('theme'); </script>

React 写法

import { createContext, useContext } from 'react'; // 1. 创建 Context const ThemeContext = createContext('light'); // 2. 父组件提供 function App() { return ( <ThemeContext.Provider value="dark"> <Child /> </ThemeContext.Provider> ); } // 3. 深层子组件消费 function Child() { const theme = useContext(ThemeContext); return <div>{theme}</div>; }

对比

Vue React
provide(‘key’, value) <Context.Provider value={…}>
inject(‘key’) useContext(Context)
按 key 取 按 Context 对象取

使用场景

· 主题切换
· 用户登录信息
· 语言国际化
· 全局配置


八、useReducer —— 相当于简化版 Pinia

作用

集中管理复杂状态逻辑,适合多个状态互相关联。

写法

import { useReducer } from 'react'; type State = { count: number }; type Action = { type: 'add' } | { type: 'sub' } | { type: 'reset' }; function reducer(state: State, action: Action): State { switch (action.type) { case 'add': return { count: state.count + 1 }; case 'sub': return { count: state.count - 1 }; case 'reset': return { count: 0 }; default: return state; } } function App() { const [state, dispatch] = useReducer(reducer, { count: 0 }); return ( <div> <p>{state.count}</p> <button onClick={() => dispatch({ type: 'add' })}>+1</button> <button onClick={() => dispatch({ type: 'sub' })}>-1</button> <button onClick={() => dispatch({ type: 'reset' })}>重置</button> </div> ); }

对比 Pinia

Pinia useReducer
state state
actions dispatch(action)
直接调用 action 派发 action 对象

使用场景

· 状态逻辑复杂,多个值互相影响
· 状态更新逻辑想集中管理
· 配合 useContext 做轻量全局状态

useState vs useReducer

// 简单状态用 useState const [count, setCount] = useState(0); // 复杂状态用 useReducer const [state, dispatch] = useReducer(reducer, initialState);

九、自定义 Hook —— 相当于 Vue 的组合式函数

作用

把可复用逻辑抽出来,名字必须以 use 开头。

Vue 写法

// useCounter.tsimport{ref}from'vue';exportfunctionuseCounter(initial=0){constcount=ref(initial);constadd=()=>count.value++;return{count,add};}

React 写法

// useCounter.ts import { useState } from 'react'; export function useCounter(initial = 0) { const [count, setCount] = useState(initial); const add = () => setCount(c => c + 1); return { count, add }; } // 使用 function App() { const { count, add } = useCounter(0); return <button onClick={add}>{count}</button>; }

使用场景

· 封装请求逻辑 useRequest
· 封装表单 useForm
· 封装本地存储 useLocalStorage
· 封装窗口大小 useWindowSize

常用示例

// useLocalStorage function useLocalStorage<T>(key: string, initial: T) { const [value, setValue] = useState<T>(() => { const saved = localStorage.getItem(key); return saved ? JSON.parse(saved) : initial; }); useEffect(() => { localStorage.setItem(key, JSON.stringify(value)); }, [key, value]); return [value, setValue] as const; }

十、钩子使用规则(必须遵守)

React 钩子有两条铁律:

  1. 只能在函数组件或自定义 Hook 顶层调用
// ❌ 不能在条件里 if (cond) { useState(0); } // ❌ 不能在循环里 for (let i = 0; i < 3; i++) { useState(0); } // ✅ 只能在顶层 const [a] = useState(0);
  1. 不能在普通函数里调用
// ❌ function helper() { useState(0); } // ✅ 自定义 Hook function useHelper() { useState(0); }

原因:React 靠调用顺序识别每个 Hook,顺序乱了就出错。


十一、Vue 转 React 常见误区

  1. 直接改 state
// ❌ count++; // ✅ setCount(count + 1);
  1. 以为 setState 是同步的
setCount(count + 1); console.log(count); // 还是旧值

想拿新值:

setCount(prev => { console.log(prev + 1); return prev + 1; });
  1. useEffect 依赖写不全
// ❌ count 是旧值 useEffect(() => { console.log(count); }, []); // ✅ useEffect(() => { console.log(count); }, [count]);
  1. 对象/数组 state 直接改
// ❌ user.name = 'Bob'; setUser(user); // ✅ 创建新对象 setUser({ ...user, name: 'Bob' });
  1. 以为 useEffect 等于 watch

useEffect 是渲染后执行,不是数据一变立刻执行,且它处理的是副作用,不是纯计算。


十二、速查表

Hook Vue 对应 作用 常用场景
useState ref / reactive 声明状态 计数器、表单
useEffect watch + 生命周期 副作用 请求、订阅、定时器
useMemo computed 缓存值 复杂计算
useCallback 无 缓存函数 传给子组件的函数
useRef 模板 ref / 普通变量 DOM 引用 / 不渲染变量 聚焦、定时器 ID
useContext provide/inject 跨层传值 主题、用户信息
useReducer Pinia 简化版 复杂状态 多状态关联
自定义 Hook 组合式函数 复用逻辑 请求、表单封装


十三、一句话总结

· useState:声明响应式数据,像 ref。
· useEffect:处理副作用,像 watch + 生命周期。
· useMemo:缓存计算结果,像 computed。
· useCallback:缓存函数引用。
· useRef:拿 DOM 或存不渲染的值。
· useContext:跨层传值,像 provide/inject。
· useReducer:复杂状态管理,像简化版 Pinia。
· 自定义 Hook:复用逻辑,像 Vue 组合式函数。
· 两条铁律:只在顶层调用,只在组件或 Hook 里调用。

Vue 是「自动挡」,React 是「手动挡」,钩子就是你手动控制渲染和副作用的工具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询