最强react hooks汇总 一次把基础hooks消化完毕

1,145 阅读15分钟

React Hooks 是从 React 16.8 版本开始引入的新特性 目前最新版本 React v18.3.1,接下来挨个介绍react Hooks的每个基础用法和差异性说明。react源码 github仓库地址,都2024年了不叨叨class组件了(反正都兼容使用) 老项目自己升级或者慢慢啃class组件语法+生命周期吧~ 以下都全面拥抱hooks

Hook 的名称必须永远以 use 开头 

React 应用是由组件构成,而组件由内置或自定义 Hook 构成。可能你经常使用别人写的自定义 Hook,但偶尔也要自己写!

你必须遵循以下这些命名公约:

  1. React 组件名称必须以大写字母开头,比如 StatusBar 和 SaveButton。React 组件还需要返回一些 React 能够显示的内容,比如一段 JSX。
  2. Hook 的名称必须以 use 开头,然后紧跟一个大写字母,就像内置的 useState 或者本文早前的自定义 useOnlineStatus 一样。Hook 可以返回任意值。

这个公约保证你始终能一眼识别出组件并且知道它的 state,Effect 以及其他的 React 特性可能“隐藏”在哪里。

useState:用于在函数组件中管理状态。

import React, { useState } from 'react';

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

    return (
        <div>
            <p>当前计数: {count}</p>
            <button onClick={() => setCount(count + 1)}>增加</button>
        </div>
    );
}

useState 是 React 中最基本也是最常用的 Hook 之一,主要用于在函数组件中管理状态。以下是对 useState 的深度解读,包括其基本用法、特性、注意事项和一些常见的陷阱。

  1. 基本用法

useState 的基本语法如下:

const [state, setState] = useState(initialState);
  • 初始值initialState 是状态的初始值,可以是任意类型,如数值、字符串、对象或数组。
  • 状态变量state 是当前状态值。
  • 状态更新函数setState 是一个函数,用于更新状态。
import React, { useState } from 'react';

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

    return (
        <div>
            <p>当前计数: {count}</p>
            <button onClick={() => setCount(count + 1)}>增加</button>
            <button onClick={() => setCount(count - 1)}>减少</button>
        </div>
    );
}
  1. 状态更新

使用 setState 更新状态时,有以下几种情况:

  • 2.1. 直接赋值

    setCount(newCount); // 直接赋值
    
  • 2.2. 使用函数更新

    • 当需要基于当前状态更新状态时,可以传递一个函数给 setState,确保在更新时使用的是最新的状态值。
    setCount(prevCount => prevCount + 1); // 使用上一个状态来更新当前状态
    
  1. 非同步更新

setState 是异步的。当调用 setState 时,React 不会立即更新 state,而是将在后续的渲染中进行批量更新。这一点在多个 setState 调用时尤其重要:

setCount(count + 1);
setCount(count + 1); // 可能导致 count 只加 1 次

为了确保准确的状态更新,推荐使用函数更新的方式:

setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1); // 确保每次都基于最新的状态
  1. 处理复杂状态

useState 也可以用来处理对象或数组等复杂状态。对于对象状态,更新时需要注意保持不可变性:

const [state, setState] = useState({ name: 'Alice', age: 25 });

// 更新对象中的某个属性
setState(prevState => ({ ...prevState, age: 26 }));
  1. 多个 useState 的使用

可以在同一个组件中使用多个 useState 调用来管理不同的状态:

const [count, setCount] = useState(0);
const [name, setName] = useState('Alice');
  1. 初始状态的延迟计算

如果计算初始状态值的过程开销较大,可以将其封装在一个函数中,这样可以确保只在组件初次渲染时计算一次:

const [count, setCount] = useState(() => {
    const initialCount = expensiveComputation();
    return initialCount;
});
  1. 注意事项
  • 不可变性:在更新状态时,要保持原有状态的不可变性。不要直接修改状态对象或数组。
  • 使用合适的更新方式:当依赖于先前状态进行更新时,必须使用函数式更新。
  • 异步操作:记住 setState 是异步的,可能会导致无法预期的行为,尤其是在多次调用时。
  1. 常见陷阱
  • 依赖旧状态:如果在 setState 中直接使用状态值,可能会得到过期的值,因此应该总是考虑使用函数更新形式。
  • 状态整合:在处理多个相关状态时,可以考虑将它们合并到单个状态对象中,以便更好地管理和更新。

useEffectuseEffect 是 React 中用于处理副作用的 Hook,它在函数组件中处理各种异步操作、订阅、手动操作 DOM 等,以下是useEffect的使用场景

1. 数据获取

获取数据是 useEffect 最常用的场景之一,通常在组件挂载时发起网络请求。

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

function DataFetchingComponent() {
    const [data, setData] = useState(null);
    
    useEffect(() => {
        const fetchData = async () => {
            const response = await fetch('https://api.example.com/data');
            const result = await response.json();
            setData(result);
        };

        fetchData();
    }, []); // 仅在组件挂载时执行

    return (
        <div>
            {data ? JSON.stringify(data) : 'Loading...'}
        </div>
    );
}
2. 订阅和事件监听

在组件中加入订阅或事件监听,可以在 useEffect 中进行设置和清理。

import React, { useEffect } from 'react';

function ResizeListenerComponent() {
    useEffect(() => {
        const handleResize = () => {
            console.log('Window resized');
        };

        window.addEventListener('resize', handleResize);

        // 清理副作用
        return () => {
            window.removeEventListener('resize', handleResize);
        };
    }, []); // 仅在组件挂载时设置监听器

    return <div>Resize the window and check the console.</div>;
}
3. 手动操作 DOM

需要在某个时刻直接操作 DOM 时,可以在 useEffect 中完成。

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

function FocusInputComponent() {
    const inputRef = useRef();

    useEffect(() => {
        inputRef.current.focus(); // 聚焦输入框
    }, []); // 仅在组件挂载时执行

    return <input ref={inputRef} type="text" />;
}
4. 清理定时器

使用定时器(如 setTimeout 或 setInterval)时,确保在组件卸载或依赖项变化时清理这些定时器。

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

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

    useEffect(() => {
        const timer = setInterval(() => {
            setCount(prevCount => prevCount + 1);
        }, 1000);

        // 清理定时器
        return () => clearInterval(timer);
    }, []); // 仅在组件挂载时设置定时器

    return <div>Count: {count}</div>;
}
5. 依赖项变化时执行副作用

根据特定的状态或 props 来执行副作用,可以将相关的变量放在依赖数组中。

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

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

    useEffect(() => {
        console.log(`Count changed: ${count}`);
    }, [count]); // 仅在 count 变化时执行

    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
    );
}
6. 组件卸载时执行回调

在组件卸载时需要执行某些操作(如清理任务),可以通过 useEffect 返回一个清理函数来实现。

import React, { useEffect } from 'react';

function ComponentWithCleanup() {
    useEffect(() => {
        console.log('Component mounted');

        return () => {
            console.log('Component unmounted'); // 组件卸载时执行
        };
    }, []);

    return <div>Check console for mount/unmount logs.</div>;
}

useContext:用于在组件树中共享数据,而无需手动通过每层组件传递 props。

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

const ThemeContext = createContext('light');

function ThemedButton() {
   const theme = useContext(ThemeContext);
   return <button className={theme}>我是一个按钮</button>;
}

function App() {
   return (
       <ThemeContext.Provider value="dark">
           <ThemedButton />
       </ThemeContext.Provider>
   );
}

useReducer:用于管理复杂的状态逻辑,通常在 state 要通过多个子组件共享时使用。

import React, { useReducer } from 'react';

const initialState = { count: 0 };

// `reducer` 是一个纯函数
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, initialState);

    return (
        <>
            Count: {state.count}
            <button onClick={() => dispatch({ type: 'increment' })}>增加</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>减少</button>
        </>
    );
}
  1. 基本用法

useReducer 的基本语法如下:

const [state, dispatch] = useReducer(reducer, initialState);
  • reducer:一个函数,接受当前状态和一个动作的描述,返回新的状态。
  • initialState:状态的初始值。
  • state:当前状态。
  • dispatch:用于发送动作的函数,通过调用它来触发状态更新。
  1. 创建 reducer 函数

reducer 是一个纯函数,接收两个参数:当前状态和动作,返回新的状态。例如,下面是一个简单的计数器示例:

function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        default:
            return state; // 返回当前状态
    }
}

3. 使用 useReducer

在组件中使用 useReducer

import React, { useReducer } from 'react';

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

    return (
        <div>
            <p>当前计数: {state.count}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>增加</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>减少</button>
        </div>
    );
}
  1. 复杂状态管理

useReducer 适合管理复杂状态,尤其是在状态结构比较复杂或者依赖于多个值的情况下。例如,处理表单的状态:

const initialState = { name: '', age: 0 };

function reducer(state, action) {
    switch (action.type) {
        case 'setName':
            return { ...state, name: action.payload };
        case 'setAge':
            return { ...state, age: action.payload };
        default:
            return state;
    }
}

function UserForm() {
    const [state, dispatch] = useReducer(reducer, initialState);

    return (
        <div>
            <input
                type="text"
                value={state.name}
                onChange={e => dispatch({ type: 'setName', payload: e.target.value })}
            />
            <input
                type="number"
                value={state.age}
                onChange={e => dispatch({ type: 'setAge', payload: e.target.value })}
            />
            <p>姓名: {state.name}, 年龄: {state.age}</p>
        </div>
    );
}
  1. 与 useContext 结合使用

useReducer 常与 useContext 一起使用来实现全局状态管理:

const StateContext = React.createContext();

const initialState = {
    count: 0,
};

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

export function StateProvider({ children }) {
    const [state, dispatch] = useReducer(reducer, initialState);

    return (
        <StateContext.Provider value={{ state, dispatch }}>
            {children}
        </StateContext.Provider>
    );
}

// 在子组件中使用状态
function Counter() {
    const { state, dispatch } = useContext(StateContext);

    return (
        <div>
            <p>当前计数: {state.count}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>增加</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>减少</button>
        </div>
    );
}
  1. 注意事项
  • 命名:确保 reducer 函数是一个纯函数,不依赖于外部变量。
  • 不可变性:在更新状态时,使用不可变模式(如扩展运算符 ...)来创建新的状态副本,而不是直接修改现有状态。
  • 性能:在状态较复杂且更新频繁的情况下,useReducer 可能比 useState 更具可读性和可维护性,但记得合理评估性能消耗。

useRef:用于获取对 DOM 元素的引用或保存一个可变的值而不引起重新渲染。除了获取对应的属性值外,useRef还有一点比较重要的特性,那就是 缓存数据

保存 DOM 引用
import React, { useRef } from 'react';

function TextInputWithFocusButton() {
    const inputRef = useRef(null);

    const focusInput = () => {
        inputRef.current.focus();
    };

    return (
        <>
            <input ref={inputRef} type="text" />
            <button onClick={focusInput}>聚焦输入框</button>
        </>
    );
}
缓存可变数据
  • useRef 还可以用来存储任何可变数据,这些数据在组件的重新渲染之间保持不变。与 useState 不同的是,更新 useRef 的值不会触发组件重新渲染。
import React, { useRef } from 'react';

function TimerComponent() {
    const countRef = useRef(0);

    const handleClick = () => {
        countRef.current += 1; // 更新缓存的数据
        console.log(`Current count: ${countRef.current}`);
    };

    return (
        <div>
            <button onClick={handleClick}>Increment</button>
        </div>
    );
}

useMemo 和 useCallback:用于性能优化,减少不必要的计算和函数创建。 只要父组件的状态更新,无论有没有对自组件进行操作,子组件都会进行更新useMemo就是为了防止这点而出现的。useCallback是一个用于优化 React 应用性能的 Hook,主要目的是为了解决: 避免不必要的函数重新创建与 React.memo 结合使用性能优化(避免子组件因父组件重新渲染而重新渲染的问题)

import React, { useMemo, useCallback, useState } from 'react';

function ExpensiveComponent({ value }) {
    const computedValue = useMemo(() => {
        // 进行一些耗时的计算
        return value * 2;
    }, [value]);

    const handleClick = useCallback(() => {
        console.log('按钮被点击');
    }, []);

    return (
        <div>
            <p>计算值: {computedValue}</p>
            <button onClick={handleClick}>点击</button>
        </div>
    );
}

useMemo & useCallback 主要区别

  • useMemo:用于缓存计算结果。它返回的是计算结果,只有在依赖项变化时重新计算。

    const memoizedValue = useMemo(() => {
        return expensiveCalculation(input);
    }, [input]);
    
  • useCallback:用于缓存函数。它返回的是一个函数,只有在依赖项变化时才会返回一个新的函数引用。

    const memoizedCallback = useCallback(() => {
        doSomething(input);
    }, [input]);
    

使用场景

  1. useMemo

    • 用于缓存计算开销较大的值,以避免在每次渲染时都重复计算。
  2. useCallback

    • 用于防止在父组件重新渲染时,传递给子组件的函数重新创建。这对于性能优化尤为重要,尤其是当子组件使用 React.memo 时。如果父组件中的函数引用变化,子组件也会重新渲染。使用 useCallback 可以确保只有在依赖项变化时才创建新的函数引用。

useLayoutEffect:与 useEffect 类似,但会在所有 DOM 更新完成后同步执行。

import React, { useLayoutEffect } from 'react';

function Component() {
    useLayoutEffect(() => {
        // 直接与 DOM 交互
        console.log('DOM已更新');
    });

    return <div>内容</div>;
}

useCallback 的示例,展示了如何避免子组件因父组件重新渲染而重新渲染的问题 (useCallback 必须配合memo,否则不但不会提升性能,还有可能降低性能)

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

// 子组件使用 React.memo 进行优化
const ChildComponent = React.memo(({ onClick }) => {
    console.log("ChildComponent rendered");
    return <button onClick={onClick}>Click me</button>;
});

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

    // 使用 useCallback 创建稳定的函数引用
    const handleClick = useCallback(() => {
        console.log("Button clicked");
    }, []);

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
            <ChildComponent onClick={handleClick} />
        </div>
    );
}

useId:用于生成唯一的 ID,在渲染过程中有助于支持服务端渲染。

import React, { useId } from 'react';

function InputComponent() {
    const id = useId();
    return (
        <div>
            <label htmlFor={id}>标签</label>
            <input id={id} type="text" />
        </div>
    );
}

useImperativeHandle:用于自定义组件实例值的 React Hook。它通常与 React.forwardRef 一起使用,以允许父组件通过 refs 访问子组件的特定实例方法或属性。

import React, { useImperativeHandle, useRef, forwardRef } from 'react';

// 定义一个子组件
const CustomInput = forwardRef((props, ref) => {
    const inputRef = useRef();

    // 使用 useImperativeHandle 自定义暴露给父组件的实例值
    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus(); // 聚焦输入框
        },
        getValue: () => {
            return inputRef.current.value; // 获取输入框的值
        }
    }));

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

在父组件中使用这个自定义组件

import React, { useRef } from 'react';
import CustomInput from './CustomInput'; // 假设 CustomInput 存放在当前路径下

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

    const handleFocus = () => {
        inputRef.current.focus(); // 调用子组件暴露的聚焦方法
    };

    const handleGetValue = () => {
        alert(inputRef.current.getValue()); // 获取输入框的值
    };

    return (
        <div>
            <CustomInput ref={inputRef} />
            <button onClick={handleFocus}>聚焦输入框</button>
            <button onClick={handleGetValue}>获取输入值</button>
        </div>
    );
}

export default ParentComponent;

解释

  • forwardRef:用于转发 refs,使得父组件能够访问子组件的 ref。

  • useImperativeHandle:用于定义父组件通过 ref 访问子组件的哪些方法和属性。它接受两个参数:

    • 第一个参数是传入的 ref
    • 第二个参数是一个函数,返回一个对象,包含要暴露给父组件的实例方法和属性。

注意事项

  • 使用 useImperativeHandle 时,通常需要与 forwardRef 一起使用,否则 ref 不会正确工作。
  • 避免在 useImperativeHandle 中返回过多功能,保持简洁,确保父组件只访问必要的方法。

useTransition & useDeferredValueuseTransition 和 useDeferredValue 是 React 18 中引入的两个新的 Hooks,旨在帮助开发者更好地管理 UI 的渲染优先级,从而提供更流畅的用户体验。

1. useTransition

useTransition 允许在处理较高延迟的状态更新时分离 UI 更新的紧急性。它返回一个布尔值,表示当前是否在过渡状态,并且提供了一个函数用于启动过渡。

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

function TransitionComponent() {
    const [isPending, startTransition] = useTransition();
    const [inputValue, setInputValue] = useState('');
    const [list, setList] = useState([]);

    const handleChange = (e) => {
        const value = e.target.value;
        setInputValue(value);

        // 使用 startTransition 来表示这个更新是非紧急的
        startTransition(() => {
            const newList = []; // 根据输入值生成新列表
            for (let i = 0; i < 10000; i++) {
                newList.push(value + i);
            }
            setList(newList);
        });
    };

    return (
        <div>
            <input type="text" value={inputValue} onChange={handleChange} />
            {isPending ? <p>Loading...</p> : null}
            <ul>
                {list.map((item, index) => (
                    <li key={index}>{item}</li>
                ))}
            </ul>
        </div>
    );
}
2. useDeferredValue

useDeferredValue 用于将某个状态的更新延迟,以便可以优先渲染其他更紧急的 UI 更新。在某些情况下,当你希望一些非紧急的输入更新不会阻塞其他更新时,这个 Hook 会非常有用。

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

function DeferredValueComponent() {
    const [inputValue, setInputValue] = useState('');
    const deferredValue = useDeferredValue(inputValue);

    return (
        <div>
            <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
            <p>Immediate Value: {inputValue}</p>
            <p>Deferred Value: {deferredValue}</p>
            <HeavyComponent value={deferredValue} />
        </div>
    );
}

function HeavyComponent({ value }) {
    // 模拟某个开销较大的组件
    const items = Array.from({ length: 10000 }, (_, i) => `${value} - Item ${i}`);
    
    return (
        <ul>
            {items.map((item, index) => (
                <li key={index}>{item}</li>
            ))}
        </ul>
    );
}
  • useTransition:用于分离非紧急的状态更新,使得用户界面保持响应性。它适用于需要处理较大列表或复杂的数据更新时,可以通过 startTransition 指明哪些更新是非紧急的。
  • useDeferredValue:用于将某个状态的更新延迟,使得在快速输入时不会阻塞重要 UI 更新。它常常用于确保界面的响应性,同时处理大量数据时不会影响用户的输入体验

自定义Hooks

自定义 Hooks 是 React 中一个强大的功能,它允许开发者将逻辑提取到可重用的函数中,从而实现功能的复用以及清晰的代码结构。自定义 Hooks 以函数的形式存在,它们可以使用其他 hooks(如 useStateuseEffect 等)。

1. 创建自定义 Hook

可以根据特定的需求创建自定义 Hook。下面是一个简单的示例,用于跟踪窗口大小:

import { useState, useEffect } from 'react';

// 自定义 Hook: useWindowSize
function useWindowSize() {
    const [windowSize, setWindowSize] = useState({
        width: window.innerWidth,
        height: window.innerHeight,
    });

    useEffect(() => {
        const handleResize = () => {
            setWindowSize({
                width: window.innerWidth,
                height: window.innerHeight,
            });
        };

        window.addEventListener('resize', handleResize);

        return () => window.removeEventListener('resize', handleResize);
    }, []);

    return windowSize;
}
2. 在组件中使用自定义 Hook

定义完自定义 Hook 后,可以在函数组件中使用它:

import React from 'react';
import useWindowSize from './useWindowSize'; // 假设自定义 Hook 存在此路径

function WindowInfo() {
    const { width, height } = useWindowSize();

    return (
        <div>
            <h1>窗口大小:</h1>
            <p>宽度: {width}px</p>
            <p>高度: {height}px</p>
        </div>
    );
}
3. 复用逻辑

自定义 Hook 可以被多个组件复用,进而提高代码的可维护性。例如,可以创建一个处理输入值的自定义 Hook:

import { useState } from 'react';

// 自定义 Hook: useInput
function useInput(initialValue) {
    const [value, setValue] = useState(initialValue);

    const handleChange = (event) => {
        setValue(event.target.value);
    };

    return {
        value,
        onChange: handleChange,
    };
}

// 在组件中使用自定义输入 Hook
function TextInput() {
    const input = useInput('');

    return (
        <div>
            <input type="text" {...input} />
            <p>当前输入: {input.value}</p>
        </div>
    );
}
4. 参数化自定义 Hook

自定义 Hook 还可以接收参数,以便在不同的上下文中进行更灵活的使用:

import { useState, useEffect } from 'react';

// 自定义 Hook: useFetch
function useFetch(url) {
    const [data, setData] = useState(null);
    const [error, setError] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        const fetchData = async () => {
            try {
                const response = await fetch(url);
                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }
                const result = await response.json();
                setData(result);
            } catch (error) {
                setError(error);
            } finally {
                setLoading(false);
            }
        };

        fetchData();
    }, [url]); // 依赖于 URL

    return { data, error, loading };
}

// 在组件中使用自定义 Hook
function DataDisplay({ url }) {
    const { data, error, loading } = useFetch(url);

    if (loading) return <p>Loading...</p>;
    if (error) return <p>Error: {error.message}</p>;

    return <div>{JSON.stringify(data)}</div>;
}

Hooks使用注意事项

  • 假设任何以 use 开头并紧跟着一个大写字母的函数就是一个 Hook
  • 只能在函数内部的最外层调用 Hook,不要在循环、条件判断或者子函数中调用
  • 只能在 React 的函数组件中调用 Hook,不要在普通JS函数中调用

hooks 归类

1. 状态管理
  • useState:允许在函数组件中添加状态管理。通过调用 useState,可以创建状态变量以及更新该状态的函数,适用于简单及局部状态管理。
2. 副作用处理
  • useEffect:用于处理副作用,例如数据获取、订阅、DOM 操作等。它可以替代类组件中的生命周期方法。useEffect 可以通过依赖性数组控制何时执行副作用,确保在组件挂载、更新或卸载时执行相关逻辑。
3. 性能优化
  • useMemo:用于缓存计算结果,避免在每次渲染时重复计算。特别适用于昂贵的计算操作。
  • useCallback:用于缓存函数,确保函数引用的稳定性,从而避免不必要的子组件渲染,尤其是在与 React.memo 结合使用时。
4. 组件间状态共享
  • useContext:使得在组件树中共享状态或数据变得简单。可以在多个深层嵌套的组件间传递和使用数据,而无需手动逐层传递 props。
5. 复杂状态管理
  • useReducer:适用于管理复杂状态和状态变化逻辑,类似于 Redux 中的 reducer。通过将状态和更新逻辑合并,可以更清晰地管理和更新状态,特别是在状态结构复杂的情况下。
6. 引用管理
  • useRef:用于获取对 DOM 元素的引用,或存储可变的值而不引起组件的重新渲染。useRef 还可以用于缓存数据。
7. 过渡效果和延迟值
  • useTransition:用于处理用户界面更新的优先级设置,确保重要更新更快完成。
  • useDeferredValue:用于延迟更新某个状态的值,以便优先渲染不重要的内容,提高用户体验。
8. 自定义 Hooks
  • Hooks 允许开发者创建自定义 Hooks,将状态逻辑抽象并复用,使代码结构更加清晰,易于管理和测试。

总结

自定义hooks优点
  1. 代码复用

    • 自定义 Hooks 允许将组件中重复的逻辑提取出来,提高代码复用性。例如,如果多个组件需要相同的状态管理或者副作用处理,可以通过自定义 Hook 来实现避免重复代码。
  2. 逻辑抽象

    • 自定义 Hooks 能将复杂的状态逻辑和副作用管理封装在一个函数中,使组件的主逻辑更加清晰。逻辑分离可以提高代码的可读性和可维护性。
  3. 组合性

    • 自定义 Hooks 可以与其他 Hooks 组合使用,增强功能的同时保持组件的简单性。此外,开发者可以将多个自定义 Hooks 组合在一起,以实现更加复杂的功能。
  4. 易于测试

    • 自定义 Hooks 可以独立于组件进行测试,便于单元测试。逻辑的抽象使得测试更加简单和清晰。
  5. 状态管理

    • 自定义 Hooks 支持使用 useStateuseReducer 等管理状态,能够灵活处理各种状态管理需求。
自定义hooks缺点
  1. 学习曲线

    • 对于新的 React 开发者,理解和掌握自定义 Hooks 的创建和使用可能有一定的学习曲线。在使用自定义 Hook 时需要注意它们的组合和依赖关系。
  2. 抽象过度

    • 如果不合理地将逻辑抽象到自定义 Hooks 中,可能会导致组件变得过于复杂,稀释组件的直接性和可读性。在某些情况下,简单的状态管理逻辑不适合抽象成 hooks。
  3. 性能问题

    • 如果自定义 Hooks 的逻辑复杂,且未能有效管理依赖关系,可能导致性能问题,例如不必要的重新渲染或副作用。需要仔细设计自定义 Hooks 的实现方式,以避免潜在的性能问题。
  4. 调试复杂性

    • 由于自定义 Hooks 可能封装了复杂的逻辑,调试时可能会增加一些困难。调试工具虽然支持 Hooks,但在复杂的逻辑中,可能需要更多的时间来理解数据流。
  5. 命名约定

    • 自定义 Hooks 应以 use 开头命名以遵循 React 的命名约定,然而在组织和命名自定义 Hooks 时,有时可能会导致命名冲突或可读性问题。

Hooks 提高了函数组件的灵活性和可维护性,允许开发者在组件中更简洁和高效地管理状态和副作用。它们促进了更清晰的代码组织和复用,使得 React 的开发体验更为出色。通过合理运用这些 Hooks,开发者可以创建功能丰富、响应迅速的 React 应用。