react 常用钩子 hooks 总结

文章目录

          • React钩子概念图
            • [状态管理钩子 state management](#状态管理钩子 state management)
            • [副作用钩子 effect hooks](#副作用钩子 effect hooks)
            • [引用钩子 Ref hooks](#引用钩子 Ref hooks)
            • 上下文钩子
            • 其他钩子
            • [过渡钩子 处理过渡效果](#过渡钩子 处理过渡效果)
            • [性能优化钩子 performance hooks](#性能优化钩子 performance hooks)
            • [React 19 新钩子](#React 19 新钩子)

React钩子概念图
状态管理钩子 state management

useState useReducer useSyncExternalStore

useState 最常用钩子

react 复制代码
const [value, setValue] = useState('')
const handleChange = (e) => {
    setValue(e.target.value)
}
<input type="text" value = {value} onChange = {handleChange}></input>
#捕获用户输入值的变化
react 复制代码
import React, { useState } from 'react';

function ToggleComponent() {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <>
      <button onClick={() => setIsVisible(!isVisible)}>Toggle</button>
      {isVisible && <div>Content to Show/Hide</div>}
    </>
  );
}

export default ToggleComponent;
# 控制显隐

useReducer 管理更为复杂状态的钩子

在一个reducer函数内管理状态

react 复制代码
import React, { useReducer } from 'react';

// Reducer 函数
const reducer = (state, action) => {
  switch (action) {
    case 'increment':
      return state + 1;
    default:
      return state; // 默认返回当前状态
  }
};

function Counter() {
  const [count, dispatch] = useReducer(reducer, 0);

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={() => dispatch('increment')}>Increment</button>
    </>
  );
}

export default Counter;

这个其实有点类似 Vue 的 vuex 两者设计思想类似 学过这两种语言的应该会有感受

相似点

  1. 状态定义
    • useReducer 中,初始状态通过 useReducer(reducer, initialState) 定义。
    • 在 Vuex 中,初始状态通过 state 属性定义。
  2. 状态更新逻辑
    • useReducer 中,reducer 函数负责根据 action 计算新的状态。
    • 在 Vuex 中,mutations 是唯一可以修改状态的地方,类似于 reducer
  3. 触发状态更新
    • useReducer 中,通过 dispatch(action) 触发状态更新。
    • 在 Vuex 中,通过 commit('mutation')dispatch('action') 触发状态更新。
  4. 集中化管理
    • 两者都提供了一种集中化的方式管理状态,避免了组件内部直接操作状态的混乱。

代码对比

React useReducer

jsx 复制代码
import React, { useReducer } from 'react';

const initialState = 0;

// Reducer 函数
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      return state;
  }
};

function Counter() {
  const [count, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </>
  );
}

export default Counter;

Vuex Store

javascript 复制代码
// Vuex store
const store = new Vuex.Store({
  state: {
    count: 0,
  },
  mutations: {
    increment(state) {
      state.count++;
    },
    decrement(state) {
      state.count--;
    },
  },
  actions: {
    increment({ commit }) {
      commit('increment');
    },
    decrement({ commit }) {
      commit('decrement');
    },
  },
});

// Vue 组件
<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

<script>
export default {
  computed: {
    count() {
      return this.$store.state.count;
    },
  },
  methods: {
    increment() {
      this.$store.dispatch('increment');
    },
    decrement() {
      this.$store.dispatch('decrement');
    },
  },
};
</script>

主要区别

特性 React useReducer Vuex
作用范围 适用于单个组件或父子组件间的状态管理 适用于全局状态管理(跨多个组件)
触发方式 dispatch(action) commit('mutation')dispatch('action')
是否支持异步操作 不直接支持,需要结合其他工具(如 useEffect 支持异步操作(通过 actions
模块化 需要手动组织代码结构 提供模块化支持(modules
学习曲线 较低,适合小型到中型应用 较高,适合中大型应用
react 复制代码
import React, { useReducer } from 'react';

// 初始状态
const initialState = { email: '', password: '' };//初始值由 initialState 定义:{ email: '', password: '' }。

// Reducer 函数
// 它接收一个 action 对象(通常包含 type 和可选的 payload),并将这个 action 传递给 reducer。
// reducer 根据 action.type 和 action.payload 计算并返回新的状态。
const reducer = (state, action) => {
  switch (action.type) {
    case 'SET_EMAIL':
      return {
        ...state,
        email: action.payload,
      };
    case 'SET_PASS':
      return {
        ...state,
        password: action.payload,
      };
    default:
      return state;
  }
};

function LoginForm() {
  const [state, dispatch] = useReducer(reducer, initialState); // state 是当前的状态对象,在这里它是一个包含 email 和 password 的对象。
    //每次调用 dispatch 时,state 会根据 reducer函数 的逻辑更新为新的值。

  return (
    <form>
      {/* 绑定 email 输入框 */}
      <input
        type="email"
        value={state.email}
        onChange={(e) => {
          dispatch({ type: 'SET_EMAIL', payload: e.target.value });
        }}
        placeholder="Enter email"
      />
      {/* 绑定 password 输入框 */}
      <input
        type="password"
        value={state.password}
        onChange={(e) => {
          dispatch({ type: 'SET_PASS', payload: e.target.value });
        }}
        placeholder="Enter password"
      />
      {/* 显示当前状态 */}
      <p>Email: {state.email}</p>
      <p>Password: {state.password}</p>
    </form>
  );
}

export default LoginForm;

useSyncExternalStore 很少用 将非react状态钩子存储到react中

副作用钩子 effect hooks

useEffect useLayoutEffect useInsertionEffect

react 复制代码
const [count, setCount] = useState(0)

useEffect(() => {
	document.title = `You clicked ${count} times`
},[count])
//给 useEffect  提供一个运行函数  传入依赖项数组 依赖项数组变化时函数运行

<button onClick={() => setCount(count + 1)}>Click Me</button>

副作用函数一般有两种 比如事件驱动副作用 点击按钮等事件 第二种 渲染驱动副作用 页面渲染后运行 调取接口

react 复制代码
const ref = useRef(null)

useEffect(() => {
    if(isPlaying){
        ref.current.play()
    }else{
        ref.current.pause()
    }
},[isPlaying])

<video ref={ref} src={src} loop playsInline />

useLayoutEffect react 绘制后运行

useEffect 异步执行 钩子不会阻塞浏览器绘制(paint),这意味着它会在浏览器完成渲染后异步执行。这是最常用的副

作用钩子,适用于大多数情况,比如数据获取、订阅或者手动DOM更新等。

useLayoutEffect 会在所有的 DOM 变更之后同步执行。这可以保证在任何浏览器绘制之前,所有的 DOM 操作已经完成。因此,如果

你的副作用涉及到 DOM 操作,并且这些操作会影响到页面的布局或视觉效果,你应该使用 useLayoutEffect 来避免闪烁或其他视觉问

题。

react 复制代码
const ref = useRef(null)
const [tooltipHeight, setTooltipHeight] = useState(0)

useLayoutEffect(() => {
    const {height} = ref.current.getBoundingClientRect()
    setTooltipHeight(height)
},[])

useInsertionEffect 在渲染过程中插入样式 它可以让你在组件渲染之前插入样式定义,从而避免样式闪烁或未定义样式的问题。很少使

引用钩子 Ref hooks

useRef useImperativeHandle

useRef 访问dom实例 ref.current

react 复制代码
const [timer, setTimer] = useState(0);
const intervalRef = useRef();
//启动计时器函数
const startTimer = () => {
    intervalRef.current = setInterval(() => {
        setTimer((prevTimer) => prevTimer + 1);
    }, 1000);
};
//startTimer 函数通过 setInterval 每隔 1 秒调用一次回调函数,该回调函数将 timer 状态增加 1。
//intervalRef.current 存储了 setInterval 返回的定时器 ID,以便稍后可以停止计时器。
// 停止计时器
const stopTimer = () => {
    clearInterval(intervalRef.current);
};

<p>Timer: {timer} seconds</p>
<button onClick={startTimer}>Start Timer</button>
<button onClick={stopTimer}>Stop Timer</button>

useImperativeHandle 也是一种引用钩子 很少使用 当你需要在父组件中操作子组件的某些功能,但又不希望暴露整个子组件实例时。

父组件获取子组件输入框的焦点

父组件

react 复制代码
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';

function ParentComponent() {
  const childRef = useRef();

  const handleFocus = () => {
    childRef.current.focusInput();
  };

  const handleClear = () => {
    childRef.current.clearInput();
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleFocus}>Focus Input</button>
      <button onClick={handleClear}>Clear Input</button>
    </div>
  );
}

export default ParentComponent;

子组件

react 复制代码
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef();

  // 自定义暴露的方法
  useImperativeHandle(ref, () => ({
    focusInput: () => {
      inputRef.current.focus();
    },
    clearInput: () => {
      inputRef.current.value = '';
    },
  }));

  return <input type="text" ref={inputRef} placeholder="Enter text" />;
});

export default ChildComponent;

useImperativeHandle 定义了两个方法:

  • focusInput:让输入框获得焦点。

  • clearInput:清空输入框的内容。

  • 父组件只能调用这两个方法,而不能直接访问 inputRef 或其他内容。

  • useImperativeHandle 必须与 forwardRef 配合使用,因为只有 forwardRef 才能将 ref 传递到子组件。

上下文钩子

useContext

读取上下文值

react 复制代码
//1 创建context
const AppContext = createContext()
//2 用Provider组件包裹
<AppContext.Provider value="你好">
	<App></App>      
</AppContext.Provider>
//3 useContext 获取值
function Title(){
    const text = useContext(AppContext)
    return <h1>{text}</h1>
}
其他钩子

useDebugValue useId

useDebugValue devtool调试用 很少使用

useId 创建唯一id 但不能用作key

react 复制代码
function Form() {
  return (
    <form>
      <EmailInput name="Email" />
      <EmailInput name="Confirm Email" />
    </form>
  );
}

function EmailInput({ name }) {
  const id = useId();

  return (
    <>
      <label htmlFor={id}>{name}</label>
      <input id={id} type="email" />
    </>
  );
}
过渡钩子 处理过渡效果

useTransition

useDeferredValue

useTransition 指定状态更新 不紧急

react 复制代码
const [filter, setFilter] = useState('');
const [inputValue, setInputValue] = useState('');
const [isPending, startTransition] = useTransition();

const filteredItems = items.filter(item => item.includes(filter));

<input
  value={inputValue}
  onChange={(event) => {
    setInputValue(event.target.value);
    startTransition(() => {
      setFilter(event.target.value);
    });
  }}
  placeholder="Type to filter..."
/>
{isPending ? <p>Loading...</p> : filteredItems.map(item => <div key={item}>{item}</div>)}

useDeferredValue 延迟状态更新

性能优化钩子 performance hooks

useMemo useCallback

useMemo 提升性能 useMemo必须返回一个值

  • useMemo 是一个 React Hook,用于缓存计算结果,避免在每次渲染时都重新计算。
react 复制代码
function SumComponent({ numbers }) {
    const sum = useMemo(() => {
        return numbers.reduce((total, n) => total + n, 0);
    }, [numbers]);
    
    return <h1>Sum: {sum}</h1>;
}
//接收一个数字数组 numbers 作为 props,并计算这些数字的总和。通过使用 useMemo,确保了只有在 numbers 发生变化时才重新计算总
//和,从而提高了组件的性能,避免了不必要的重复计算。
react 复制代码
import React, { useMemo } from 'react';

function SumComponent({ numbers }) {
  // 使用 useMemo 缓存计算结果
  const sum = useMemo(() => {
    console.log('Calculating sum...');
    return numbers.reduce((total, num) => total + num, 0);
  }, [numbers]); // 只有当 numbers 发生变化时才重新计算

  return (
    <div>
      <h1>Sum: {sum}</h1>
    </div>
  );
}

export default function App() {
  const [numbers, setNumbers] = React.useState([1, 2, 3, 4, 5]);
  const [count, setCount] = React.useState(0);

  // 添加一个随机数到数组中
  const addRandomNumber = () => {
    setNumbers([...numbers, Math.floor(Math.random() * 100)]);
  };

  return (
    <div>
      <h2>Current Count: {count}</h2>
      <button onClick={() => setCount(count + 1)}>Increase Count</button>
      <button onClick={addRandomNumber}>Add Random Number</button>
      <SumComponent numbers={numbers} />
    </div>
  );
}

useCallback 缓存回调函数

react 复制代码
function Counter() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount((c) => c + 1);
  }, []);

  return (
    <>
      <div>{count}</div>
      <Button onClick={increment} />
    </>
  );
}

function Button({ onClick }) {
  return <button onClick={onClick}>Click me</button>;
}
React 19 新钩子

useFormStatus useFormState useOptimistic use

相关推荐
去伪存真3 分钟前
不用动脑,手把手跟着我做,就能掌握Gitlab+Jenkins提交代码自动构部署
前端·jenkins
天天扭码39 分钟前
深入解析 JavaScript 中的每一类函数:从语法到对比,全面掌握适用场景
前端·javascript·面试
小希爸爸1 小时前
4、中医基础入门和养生
前端·后端
kooboo china.1 小时前
Tailwind CSS 实战:基于 Kooboo 构建企业官网页面(一)
前端·css·编辑器
uhakadotcom1 小时前
Fluid:云原生数据加速与管理的简单入门与实战
前端
鬼面瓷1 小时前
CAPL编程_03
前端·数据库·笔记
帅云毅1 小时前
Web漏洞--XSS之订单系统和Shell箱子
前端·笔记·web安全·php·xss
北上ing2 小时前
同一页面下动态加载内容的两种方式:AJAX与iframe
前端·javascript·ajax
纪元A梦2 小时前
华为OD机试真题——推荐多样性(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
java·javascript·c++·python·华为od·go·华为od机试题
小墨宝3 小时前
js 生成pdf 并上传文件
前端·javascript·pdf