对比react钩子函数

有 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 写法

vue 复制代码
<script setup>
import { ref } from 'vue';
const count = ref(0);

function add() {
  count.value++;
}
</script>

<template>
  <button @click="add">{{ count }}</button>
</template>

React 写法

tsx 复制代码
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 触发更新

使用场景

· 计数器、表单输入、开关状态、列表数据

· 任何需要「变了就刷新页面」的数据

注意

tsx 复制代码
// ❌ 不能直接改
count++;

// ✅ 用 set 函数
setCount(count + 1);

// ✅ 依赖旧值时用函数式
setCount(prev => prev + 1);

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

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

作用

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

三种形态

tsx 复制代码
// 1. 每次渲染都执行
useEffect(() => {
  console.log('渲染了');
});

// 2. 只在挂载时执行一次(相当于 onMounted)
useEffect(() => {
  console.log('挂载');
}, []);

// 3. 依赖变化时执行(相当于 watch)
useEffect(() => {
  console.log('count 变了', count);
}, [count]);

清理函数(相当于 onUnmounted)

tsx 复制代码
useEffect(() => {
  const timer = setInterval(() => {
    console.log('tick');
  }, 1000);

  // 组件卸载或依赖变化前执行
  return () => {
    clearInterval(timer);
  };
}, []);

Vue 对比

vue 复制代码
<script setup>
import { watch, onMounted, onUnmounted } from 'vue';

onMounted(() => {
  console.log('挂载');
});

watch(count, (newVal) => {
  console.log('count 变了', newVal);
});

onUnmounted(() => {
  console.log('卸载');
});
</script>
tsx 复制代码
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() }, \[\])

依赖数组规则

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

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


四、useMemo ------ 相当于 computed

作用

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

Vue 写法

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 写法

tsx 复制代码
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 的依赖时保持引用稳定

注意

tsx 复制代码
// 简单计算不用 useMemo,反而增加开销
const double = count * 2; // 直接算就行

// 复杂计算才用
const sorted = useMemo(() => bigList.sort(...), [bigList]);

五、useCallback ------ 缓存函数

作用

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

为什么需要?

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

写法

tsx 复制代码
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 的依赖

注意

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

useMemo 缓存值,useCallback 缓存函数。

tsx 复制代码
// 等价
useCallback(fn, deps) === useMemo(() => fn, deps)

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

作用

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

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

  3. 获取 DOM

Vue:

vue 复制代码
<template>
  <input ref="inputRef" />
</template>

<script setup>
import { ref, onMounted } from 'vue';
const inputRef = ref(null);

onMounted(() => {
  inputRef.value.focus();
});
</script>

React:

tsx 复制代码
import { useRef, useEffect } from 'react';

function App() {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} />;
}
  1. 保存变量(改了不重新渲染)
tsx 复制代码
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 写法

vue 复制代码
<!-- 父 -->
<script setup>
import { provide } from 'vue';
provide('theme', 'dark');
</script>

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

React 写法

tsx 复制代码
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

作用

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

写法

tsx 复制代码
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

tsx 复制代码
// 简单状态用 useState
const [count, setCount] = useState(0);

// 复杂状态用 useReducer
const [state, dispatch] = useReducer(reducer, initialState);

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

作用

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

Vue 写法

ts 复制代码
// useCounter.ts
import { ref } from 'vue';

export function useCounter(initial = 0) {
  const count = ref(initial);
  const add = () => count.value++;
  return { count, add };
}

React 写法

tsx 复制代码
// 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

常用示例

tsx 复制代码
// 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 顶层调用
tsx 复制代码
// ❌ 不能在条件里
if (cond) {
  useState(0);
}

// ❌ 不能在循环里
for (let i = 0; i < 3; i++) {
  useState(0);
}

// ✅ 只能在顶层
const [a] = useState(0);
  1. 不能在普通函数里调用
tsx 复制代码
// ❌
function helper() {
  useState(0);
}

// ✅ 自定义 Hook
function useHelper() {
  useState(0);
}

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


十一、Vue 转 React 常见误区

  1. 直接改 state
tsx 复制代码
// ❌
count++;

// ✅
setCount(count + 1);
  1. 以为 setState 是同步的
tsx 复制代码
setCount(count + 1);
console.log(count); // 还是旧值

想拿新值:

tsx 复制代码
setCount(prev => {
  console.log(prev + 1);
  return prev + 1;
});
  1. useEffect 依赖写不全
tsx 复制代码
// ❌ count 是旧值
useEffect(() => {
  console.log(count);
}, []);

// ✅
useEffect(() => {
  console.log(count);
}, [count]);
  1. 对象/数组 state 直接改
tsx 复制代码
// ❌
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 是「手动挡」,钩子就是你手动控制渲染和副作用的工具。

相关推荐
阿狗童鞋1 天前
React实战指南
前端·react.js·前端框架
用户298698530141 天前
React 中实现 Word 与 Markdown 双向转换的实践记录
javascript·react.js·markdown
JavaPub-rodert1 天前
开源一套 Go + React 后台管理系统,支持 RBAC 权限管理、Docker 一键部署!
react.js·golang·开源
kill5222 天前
useRef
前端·javascript·react.js
西柚小萌新2 天前
【LLM&&AI应用开发 八股文】--4.2.Agent智能体(中)
前端·javascript·react.js
Java后端的Ai之路2 天前
03_React_JSX
前端·react.js·前端框架
晴天小庭3 天前
Sael——基于Jev的AI中转站的安全风控网关,现已开源
人工智能·后端·react.js
Java后端的Ai之路3 天前
01-React基础教程
开发语言·前端·python·react.js·前端框架