0
0
0

React Hook学习

2025-12-03
2026-10-22

参考链接: React 内置 Hook

Hook简单介绍

** 定义:** 以use开头的函数

** 作用:** Hook有助于在组件中使用不同的React功能

** 使用:** Hook比普通函数更为严格,只能在的组件(或其他 Hook)的 顶层 调用Hook

** 分类:**

  • State Hook
  • Context Hook
  • Ref Hook
  • Effect Hook
  • 性能 Hook
  • 其他 Hook
  • 自定义 Hook

State Hook

状态帮助组件,用于“记住”用户的输入信息

useState :声明可以直接更新的状态变量

示例:

function MyButton(){
  const [count,setCount] = useState(0);
  
  function handleClick(){
    setCount(count+1);
  }
  return(
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  )
}

useReducer:在reducer函数中声明带有更新逻辑的state变量

reducer函数 :将组件的所有状态更新逻辑整合到一个外部函数中,这个函数叫做reducer

useReducer钩子 接受参数:

  • 一个reducer函数
  • 一个初始的state 返回内容:
  • 一个有状态的值
  • 一个dispatch函数(派发用户操作给reducer)

示例: {tabs} {tabs-pane label="App.js"}

import { useReducer } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';
import tasksReducer from './tasksReducer.js';

export default funtion TaskApp(){
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
  function handleAddTask(text){
    dispatch({
      type:'added',
      id:nextId++,
      text:text,
    });
  }
  function handleChangeTask(task){
    dispatch({
      type:'changed',
      task:task
    });
  }
  function handleDeleteTask(taskId){
    dispatch({
      type:'deleted',
      id:taskId,
    });
  }

  return (
    <>
      <h1>布拉格的行程安排</h1>
      <AddTask onAddTask={handleAddTask} />
      <TaskList
        tasks={tasks}
        onChangeTask={handleChangeTask}
        onDeleteTask={handleDeleteTask}
      />
    </>
  );
}

{/tabs-pane} {tabs-pane label="tasksReducer.js"}

export default function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case 'changed': {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('未知 action:' + action.type);
    }
  }
}


{/tabs-pane} {/tabs}

Context Hook

上下文帮助组件 从祖先组件接收信息,而无需将其作为 props 传递

useContext 读取订阅上下文

示例:

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

export default function MyApp(){
  return (
    <ThemeContext.Provider value="dark">
      <Form/>
    <ThemeContext.Provider>
  );
}

function Form(){
  return (
    <Panel title="Welcome">
      <Button>Sign up </Button>
      <Button>Log in  </Button>
    </Panel>
  );
}
function Panel({title,children}){
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return(
    <section className = {className}}>
      <h1>{title}</h1>
      {children}
    </section>
  );
}
funciton Button({children}){
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return(
    <button className={className}>
      {children}
    </button>
  );
}

**注意:**通常情况下,useContext()只会调用上层provider,当然通过合并的方式可以实现嵌套调用

Ref Hook

ref 允许组件 保存一些不用于渲染的信息,比如 DOM 节点或 timeout ID。与状态不同,更新 ref 不会重新渲染组件。

useRef 声明ref,可以保存任何值,但常用于保存DOM节点

示例:

import { useRef } from 'react';

export default function Counter() {
  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
    alert('You clicked ' + ref.current + ' times!');
  }

  return (
    <button onClick={handleClick}>
      点击!{ref.current}
    </button>
  );
}

与useState不同的是,ref不会触发重新渲染,所以JSX中显示{ref.current}不会变化

useImperativeHandle 自定义从组件中暴露ref,不常用

Effect Hook

Effect 允许组件 连接到外部系统并与之同步。这包括处理网络、浏览器、DOM、动画、使用不同 UI 库编写的小部件以及其他非 React 代码

useEffect 将组件连接到外部系统

外部系统 :有些组件需要与网络、某些浏览器 API 或第三方库保持连接,当它们显示在页面上时。这些系统不受 React 控制,所以称为外部系统

示例:

import {useState,useEffect} from 'react';
import {createConnection} from './chat.js';
function ChatRoom({roomId}){
  const [serverUrl,setServerUrl] = useState('https://localhost:1234');
  useEffect(()=>{
    const connection = createConnection(serverUrl,roomId);
    connection.connect();
    return ()=>{
      connection.disconnection();
    };
  },[serverUrl,roomId]);
  // ...
}

需要向 useEffect 传递两个参数:

  • 一个 setup 函数 ,其 setup 代码 用来连接到该系统。 它应该返回一个 清理函数(cleanup),其 cleanup 代码 用来与该系统断开连接。
  • 一个 依赖项列表,包括这些函数使用的每个组件内的值

性能 Hook

使用性能hook可以跳过计算和不必要的重新渲染来优化性能

useMemo 缓存计算代价昂贵的计算结果

useMemo(calculateValue, dependencies)

calculateValue:要缓存计算值的函数

dependencies:所有在 calculateValue 函数中使用的响应式变量组成的数组

示例: {tabs} {tabs-pane label="App.js"}

import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';

const todos = createTodos();
export default funtion App(){
  const [tab,setTab] = useState('all');
  const [isDark,setIsDark] = useState(false);
  return(
    <>
      <button onClick={()=>setTab('all')}>All</button>
      <button onClick={()=>setTab('active')}>Active</button>
      <button onClick={()=>setTab('completed')}>Completed</button>
      <br/>
      <label>
        <input type="checkbox" checked={isDark} onChange={e=>setIsDark(e.target.checked)}>
          Dark mode
          </input>
      </label>
      <hr/>
      <TodoList todos={todos} tab={tab} theme={isDark?'dark':'light'}/>
    </>
  );
}

{/tabs-pane} {tabs-pane label="TodoList.js"}

import { useMemo } from 'react';
import { filterTodos } from './utils.js'

export default function TodoList({ todos, theme, tab }) {
 const visibleTodos = useMemo(
   () => filterTodos(todos, tab),
   [todos, tab]
 );
 return (
   <div className={theme}>
     <p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p>
     <ul>
       {visibleTodos.map(todo => (
         <li key={todo.id}>
           {todo.completed ?
             <s>{todo.text}</s> :
             todo.text
           }
         </li>
       ))}
     </ul>
   </div>
 );
}

{/tabs-pane} {tabs-pane label="utils.js"}

export function createTodos() {
  const todos = [];
  for (let i = 0; i < 50; i++) {
    todos.push({
      id: i,
      text: "Todo " + (i + 1),
      completed: Math.random() > 0.5
    });
  }
  return todos;
}

export function filterTodos(todos, tab) {
  console.log('[ARTIFICIALLY SLOW] Filtering ' + todos.length + ' todos for "' + tab + '" tab.');
  let startTime = performance.now();
  while (performance.now() - startTime < 500) {
    // 在 500 毫秒内不执行任何操作以模拟极慢的代码
  }

  return todos.filter(todo => {
    if (tab === 'all') {
      return true;
    } else if (tab === 'active') {
      return !todo.completed;
    } else if (tab === 'completed') {
      return todo.completed;
    }
  });
}

{/tabs-pane} {/tabs}

useCallback 将函数传递给优化组件之前缓存函数定义

const cachedFn = useCallback(fn, dependencies)

fn:想要缓存的函数

dependencies:有关是否更新 fn 的所有响应式值的一个列表

示例:

function ProductPage({ productId, referrer, theme }) {
  // 在多次渲染中缓存函数
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]); // 只要这些依赖没有改变

  return (
    <div className={theme}>
      {/* ShippingForm 就会收到同样的 props 并且跳过重新渲染 */}
      <ShippingForm onSubmit={handleSubmit} />
    </div>
  );
}

useMemo与useCallback联系

useMemo 经常与 useCallback 一同出现,区别在于

useMemo 缓存函数调用的结果

useCallback 缓存函数本身

// 在 React 内部的简化实现
function useCallback(fn, dependencies) {
  return useMemo(() => fn, dependencies);
}

示例:

import { useMemo, useCallback } from 'react';

function ProductPage({ productId, referrer }) {
  const product = useData('/product/' + productId);

  const requirements = useMemo(() => { //调用函数并缓存结果
    return computeRequirements(product);
  }, [product]);

  const handleSubmit = useCallback((orderDetails) => { // 缓存函数本身
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);

  return (
    <div className={theme}>
      <ShippingForm requirements={requirements} onSubmit={handleSubmit} />
    </div>
  );
}

其他 Hook

  • 使用 useDebugValue 自定义 React 开发者工具为自定义 Hook 添加的标签。
  • 使用 useId 将唯一的 ID 与组件相关联,其通常与可访问性 API 一起使用。
  • 使用 useSyncExternalStore 订阅外部 store。
  • 使用 useActionState 允许你管理动作的状态.

自定义Hook

自定义hook

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

评论