React 中使用自定义 hooks

Using custom hooks in React

Posted by Reckless on June 5, 2024

在 React 中,自定义 hooks(钩子)是一个非常强大的工具,允许你将组件逻辑提取到可重用的函数中。自定义 hooks 是通过使用 React 提供的基本 hooks(如 useState、useEffect 等)来构建的。下面是编写自定义 hooks 的流程

确定 hooks 的功能

首先,需要明确自定义 hooks 的功能需求和目的。清晰地定义 hooks 要实现的功能和业务逻辑。

定义 hooks

在 React 中,自定义 hooks 是一个以 use 开头的函数。它可以使用其他 hooks 来实现特定功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { useState, useEffect } from 'react';

function useCustomHook() {
  // 初始化状态
  const [state, setState] = useState(initialState);

  // 使用 useEffect 进行副作用处理
  useEffect(() => {
    // 副作用逻辑

    // 清理函数(可选)
    return () => {
      // 清理逻辑
    };
  }, [dependencies]); // 依赖数组

  // 返回需要的状态和函数
  return [state, setState];
}

使用 hooks

在组件中使用自定义 hooks,如同使用 React 内置 hooks 一样。

1
2
3
4
5
6
7
8
9
function MyComponent() {
  const [state, setState] = useCustomHook();

  return (
      <div>
        {/* 渲染逻辑 */}
      </div>
  );
}

测试 hooks

编写单元测试来确保 hooks 的功能正确。可以使用 React Testing Library 和 Jest 进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { renderHook, act } from '@testing-library/react-hooks';
import useCustomHook from './useCustomHook';

test('should use custom hook', () => {
  const { result } = renderHook(() => useCustomHook());

  // 初始状态测试
  expect(result.current[0]).toBe(initialState);

  // 状态更新测试
  act(() => {
    result.current[1](newState);
  });
  expect(result.current[0]).toBe(newState);
});

优化和重构

根据具体需求优化和重构 hooks 的代码,确保其性能和可维护性。

示例

一个简单的计数器 hooks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { useState } from 'react';

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

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  const reset = () => setCount(initialValue);

  return { count, increment, decrement, reset };
}

// 使用示例
function CounterComponent() {
  const { count, increment, decrement, reset } = useCounter(10);

  return (
      <div>
        <p>Count: {count}</p>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={reset}>Reset</button>
      </div>
  );
}

实际案例

项目背景:多个 Echarts 图表的渲染,把图表中 option 配置对象中的变化的属性(根据请求返回的数据)单独提取出来,作为 model,再编写处理同一套数据结构 modelformat 转换函数。因为需要用状态保存处理后的数据,并做一些处理前的判断(此处判断 dataList 是否是空对象),所以有了自定义 hook useDataFormat :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const useDataFormat = (
    dataList, // 请求得到的数据列表
    dataModel, // 用于渲染数据结构,包含一些静态数据
    format,  // 格式化数据列表的函数
    additionalArgs = [] // 传入格式化列表函数的参数
) => {
  // 定义数据状态,初始值为 dataModel
  const [data, setData] = useState(dataModel)

  useEffect(() => {
    if (Object.keys(dataList).length === 0) return
    // 调用 format 函数,按照 dataModel 进行格式化
    const formattedData = format(dataList, dataModel, ...additionalArgs)
    setData(formattedData)
  }, [dataList])

  return data
}

自定义 hooks 的优势

  1. 代码重用:将通用逻辑提取到自定义 hook 中,多个组件可以共享。
  2. 逻辑分离:将与 UI 逻辑分离的业务逻辑放到 hooks 中,使组件更简洁。
  3. 状态管理:在复杂组件中使用自定义 hooks 管理状态,可以提高代码的可维护性。

通过创建自定义 hooks,React 开发者可以更好地组织和管理应用中的逻辑,使代码更干净、更易于维护。



App ready for offline use.