Skip to content

React Common Hooks

除了 useState,React 还提供了许多其他的 Hooks 用于管理组件的状态和生命周期。以下是一些常见的 Hooks,以及它们的详细解释和示例代码。

1. useState

用途useState 用于在函数组件中添加本地状态。这个状态在组件的多次渲染之间是被保持的,它返回一个状态变量和一个更新这个状态变量的函数。

基本用法

  • useState 接受初始状态作为参数,并返回一个包含两个元素的数组。
  • 第一个元素是当前的状态值,第二个元素是一个允许你更新该状态的函数。

特点

  • 使用 useState,你可以在函数组件中添加和读取状态,而无需将组件转换为类。
  • 状态的更新可能是异步的,React 可能会延迟或合并多次 setState() 调用以优化性能。

示例代码

jsx
import React, { useState } from 'react';

function Example() {
  // 声明一个新的状态变量,我们将其称为 "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
import React, { useState } from 'react';

function Example() {
  // 声明一个新的状态变量,我们将其称为 "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

伪代码

jsx
function useState(initialValue) {
    // 获取当前组件的上下文
    const currentComponent = React.__currentComponent;

    // 获取下一个状态钩子的索引
    const hookIndex = currentComponent.__nextHookIndex++;

    // 首次渲染时,设置初始值
    if (currentComponent.__isFirstRender) {
        // 初始化状态和更新函数
        currentComponent.__hooks[hookIndex] = {
            state: initialValue,
            queue: []
        };
    }

    // 获取当前的钩子
    const hook = currentComponent.__hooks[hookIndex];

    // 如果有更新排队,应用这些更新
    hook.queue.forEach(update => {
        hook.state = update(hook.state);
    });

    // 清空更新队列
    hook.queue = [];

    // setState函数,用于更新状态
    const setState = (newState) => {
        // 将更新函数加入队列
        hook.queue.push(newState instanceof Function ? newState : () => newState);

        // 触发组件的重新渲染
        React.__scheduleRender(currentComponent);
    };

    // 返回状态值和更新状态的函数
    return [hook.state, setState];
}
function useState(initialValue) {
    // 获取当前组件的上下文
    const currentComponent = React.__currentComponent;

    // 获取下一个状态钩子的索引
    const hookIndex = currentComponent.__nextHookIndex++;

    // 首次渲染时,设置初始值
    if (currentComponent.__isFirstRender) {
        // 初始化状态和更新函数
        currentComponent.__hooks[hookIndex] = {
            state: initialValue,
            queue: []
        };
    }

    // 获取当前的钩子
    const hook = currentComponent.__hooks[hookIndex];

    // 如果有更新排队,应用这些更新
    hook.queue.forEach(update => {
        hook.state = update(hook.state);
    });

    // 清空更新队列
    hook.queue = [];

    // setState函数,用于更新状态
    const setState = (newState) => {
        // 将更新函数加入队列
        hook.queue.push(newState instanceof Function ? newState : () => newState);

        // 触发组件的重新渲染
        React.__scheduleRender(currentComponent);
    };

    // 返回状态值和更新状态的函数
    return [hook.state, setState];
}

2. useEffect

用途:用于在组件渲染到屏幕之后执行副作用操作(如数据获取、订阅或手动更改 React 组件中的 DOM)。

示例代码

jsx
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

伪代码

jsx
function useEffect(effectFunction, dependencies) {
    const currentComponent = React.__currentComponent;
    const hookIndex = currentComponent.__nextHookIndex++;

    const lastDependencies = currentComponent.__hooks[hookIndex]?.lastDependencies;

    // 检查依赖是否发生变化
    const hasChanged = lastDependencies === undefined || dependencies.some((dep, i) => dep !== lastDependencies[i]);

    if (hasChanged) {
        // 清理上一次的副作用(如果存在)
        if (currentComponent.__hooks[hookIndex]?.cleanup) {
            currentComponent.__hooks[hookIndex].cleanup();
        }

        // 执行副作用函数
        currentComponent.__hooks[hookIndex] = {
            lastDependencies: dependencies,
            cleanup: effectFunction()
        };
    }
}
function useEffect(effectFunction, dependencies) {
    const currentComponent = React.__currentComponent;
    const hookIndex = currentComponent.__nextHookIndex++;

    const lastDependencies = currentComponent.__hooks[hookIndex]?.lastDependencies;

    // 检查依赖是否发生变化
    const hasChanged = lastDependencies === undefined || dependencies.some((dep, i) => dep !== lastDependencies[i]);

    if (hasChanged) {
        // 清理上一次的副作用(如果存在)
        if (currentComponent.__hooks[hookIndex]?.cleanup) {
            currentComponent.__hooks[hookIndex].cleanup();
        }

        // 执行副作用函数
        currentComponent.__hooks[hookIndex] = {
            lastDependencies: dependencies,
            cleanup: effectFunction()
        };
    }
}

3. useContext

用途:允许你在组件树中共享数据而不必显式地通过每个层级传递 props。

示例代码

jsx
import React, { useContext } from 'react';

const MyContext = React.createContext();

function Example() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
}
import React, { useContext } from 'react';

const MyContext = React.createContext();

function Example() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
}

伪代码

jsx
function useContext(context) {
    // 获取当前组件的上下文
    const currentComponent = React.__currentComponent;

    // 检索上下文的当前值
    const contextValue = React.__getContextValue(context);

    // 组件订阅上下文的变化
    React.__subscribeToContext(context, currentComponent);

    // 返回上下文的当前值
    return contextValue;
}
function useContext(context) {
    // 获取当前组件的上下文
    const currentComponent = React.__currentComponent;

    // 检索上下文的当前值
    const contextValue = React.__getContextValue(context);

    // 组件订阅上下文的变化
    React.__subscribeToContext(context, currentComponent);

    // 返回上下文的当前值
    return contextValue;
}

4. useReducer

用途:用于更复杂的组件状态逻辑,它接受一个状态更新的函数和初始状态,返回当前的状态和一个让你能够触发状态更新的 dispatch 方法。

示例代码

jsx
import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

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

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </>
  );
}
import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

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

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </>
  );
}

伪代码

jsx
function useReducer(reducer, initialState) {
    // state 用于存储当前状态
    let state = initialState;

    // dispatch 函数用于触发状态更新
    const dispatch = (action) => {
        // 调用 reducer 函数来计算新状态
        state = reducer(state, action);
        // 这里简化了更新组件的逻辑。
        // 在真实的 React useReducer 中,会触发组件的重新渲染
    };

    // 返回当前状态和 dispatch 函数
    return [state, dispatch];
}

// reducer 函数定义了如何响应不同的动作并更新状态
function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return state + 1;
        case 'decrement':
            return state - 1;
        default:
            throw new Error();
    }
}

// 使用 useReducer
const [state, dispatch] = useReducer(reducer, 0);

// 触发状态更新
dispatch({ type: 'increment' }); // state 现在是 1
dispatch({ type: 'decrement' }); // state 现在是 0
function useReducer(reducer, initialState) {
    // state 用于存储当前状态
    let state = initialState;

    // dispatch 函数用于触发状态更新
    const dispatch = (action) => {
        // 调用 reducer 函数来计算新状态
        state = reducer(state, action);
        // 这里简化了更新组件的逻辑。
        // 在真实的 React useReducer 中,会触发组件的重新渲染
    };

    // 返回当前状态和 dispatch 函数
    return [state, dispatch];
}

// reducer 函数定义了如何响应不同的动作并更新状态
function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return state + 1;
        case 'decrement':
            return state - 1;
        default:
            throw new Error();
    }
}

// 使用 useReducer
const [state, dispatch] = useReducer(reducer, 0);

// 触发状态更新
dispatch({ type: 'increment' }); // state 现在是 1
dispatch({ type: 'decrement' }); // state 现在是 0

5. useRef

用途:用于获取对 DOM 元素的直接访问,或者存储任何可变值,该值不会导致组件重新渲染。

示例代码

jsx
import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

伪代码

jsx
function useRef(initialValue) {
    // useRef 使用一个对象来存储引用的值,这个对象有一个叫做 'current' 的属性
    const refObject = { current: initialValue };

    // 返回这个对象
    return refObject;
}

// 使用 useRef
const myRef = useRef(0);

// 访问 ref 的值
console.log(myRef.current); // 输出 0

// 更新 ref 的值
myRef.current = 10;

// 再次访问 ref 的值
console.log(myRef.current); // 输出 10
function useRef(initialValue) {
    // useRef 使用一个对象来存储引用的值,这个对象有一个叫做 'current' 的属性
    const refObject = { current: initialValue };

    // 返回这个对象
    return refObject;
}

// 使用 useRef
const myRef = useRef(0);

// 访问 ref 的值
console.log(myRef.current); // 输出 0

// 更新 ref 的值
myRef.current = 10;

// 再次访问 ref 的值
console.log(myRef.current); // 输出 10

6. useMemo

用途:用于优化性能,它可以记住一个计算得出的值,并且只有在其依赖项改变时才重新计算。

示例代码

jsx
import React, { useMemo } from 'react';

function Example({ a, b }) {
  const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  return <div>{memoizedValue}</div>;
}
import React, { useMemo } from 'react';

function Example({ a, b }) {
  const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  return <div>{memoizedValue}</div>;
}

7. useCallback

用途:返回一个记忆化的回调函数,该回调函数仅在某个依赖项改变时才会更新。

示例代码

jsx
import React, { useCallback } from 'react';

function Example({ onIncrement }) {
  const memoizedCallback = useCallback(
    () => {
      onIncrement();
    },
    [onIncrement],
  );
  return <button onClick={memoizedCallback}>Increment</button>;
}
import React, { useCallback } from 'react';

function Example({ onIncrement }) {
  const memoizedCallback = useCallback(
    () => {
      onIncrement();
    },
    [onIncrement],
  );
  return <button onClick={memoizedCallback}>Increment</button>;
}

伪代码

jsx
function useMemo(factory, deps) {
    // 用于存储记忆化值的变量
    let memoizedValue;

    // 用于存储依赖数组的上一次值的变量
    let oldDeps = deps;

    if (depsChanged(oldDeps, deps)) {
        // 如果依赖项改变了,重新计算值
        memoizedValue = factory();
        oldDeps = deps;
    }

    // 返回记忆化的值
    return memoizedValue;
}

function depsChanged(oldDeps, deps) {
    // 比较旧的依赖项和新的依赖项,判断它们是否相等
    // 这里简化了实际的比较逻辑
    return oldDeps !== deps;
}

// 使用 useMemo
const expensiveValue = useMemo(() => {
    // 进行复杂计算
    return computeExpensiveValue(a, b);
}, [a, b]);

// computeExpensiveValue 只会在 a 或 b 改变时重新计算
function useMemo(factory, deps) {
    // 用于存储记忆化值的变量
    let memoizedValue;

    // 用于存储依赖数组的上一次值的变量
    let oldDeps = deps;

    if (depsChanged(oldDeps, deps)) {
        // 如果依赖项改变了,重新计算值
        memoizedValue = factory();
        oldDeps = deps;
    }

    // 返回记忆化的值
    return memoizedValue;
}

function depsChanged(oldDeps, deps) {
    // 比较旧的依赖项和新的依赖项,判断它们是否相等
    // 这里简化了实际的比较逻辑
    return oldDeps !== deps;
}

// 使用 useMemo
const expensiveValue = useMemo(() => {
    // 进行复杂计算
    return computeExpensiveValue(a, b);
}, [a, b]);

// computeExpensiveValue 只会在 a 或 b 改变时重新计算