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

相关推荐
wyiyiyi21 分钟前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip44 分钟前
vite和webpack打包结构控制
前端·javascript
excel1 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国1 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼1 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy2 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
ZXT2 小时前
promise & async await总结
前端
Jerry说前后端2 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化
画个太阳作晴天2 小时前
A12预装app
linux·服务器·前端
7723892 小时前
解决 Microsoft Edge 显示“由你的组织管理”问题
前端·microsoft·edge