打破限制!自定义 Hooks 如何提升 React 组件的灵活性

本周开发监控项目,我发现了很多的 React 类组件封装,发现出现了多次UI渲染的情况、代码辨识度也较差,对性能和维护都产生了挑战。这里多个场景的都是状态管理和逻辑复用需求,其实完全没有必要封装类组件。相反我通过引入 React 自定义 Hook,通过逻辑抽离的方式,不仅有效地找到了解决办法,也提高了代码复用性。

背景

通常情况下,我们习惯将业务逻辑和 UI 一起封装到组件中,然而随着项目需求的增加,某些复杂的状态管理和副作用逻辑经常需要在多个组件中复用。传统的组件封装方式在这种场景下复用性较差,且每次使用时都可能引入额外的 UI 代码,增加了维护成本。

为了解决这一问题,我们可以将一些常见的逻辑抽离出来,封装成自定义 Hook,供多个组件复用。下面我们来比较一下不使用自定义 Hooks 和使用自定义 Hooks 的代码。

场景复现对比

1、不使用自定义 Hooks

每个表单组件都需要单独管理 useState 和验证逻辑,代码会变得冗长且难以维护。

import React, { Component } from 'react';

class UserForm extends Component {
  constructor(props) {
    super(props);
    this.state = { name: '', workcode: '' };
  }

  handleChange = (event) => {
    this.setState({ [event.target.name]: event.target.value });
  };

  handleSubmit = (event) => {
    event.preventDefault();
    console.log('handleSubmit:', this.state);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          name="name"
          type="text"
          value={this.state.name}
          onChange={this.handleChange}
          placeholder="Name"
        />
        <input
          name="workcode"
          type="text"
          value={this.state.workcode}
          onChange={this.handleChange}
          placeholder="Workcode"
        />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

class AdminForm extends Component {
  constructor(props) {
    super(props);
    this.state = { name: '', workcode: '' };
  }

  handleChange = (event) => {
    this.setState({ [event.target.name]: event.target.value });
  };

  handleSubmit = (event) => {
    event.preventDefault();
    console.log('handleSubmit:', this.state);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          name="name"
          type="text"
          value={this.state.name}
          onChange={this.handleChange}
          placeholder="Name"
        />
        <input
          name="workcode"
          type="workcode"
          value={this.state.workcode}
          onChange={this.handleChange}
          placeholder="workcode"
        />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

这种不断重复地逻辑,每个表单组件都会有类似的状态管理,以及类似的逻辑操作。而且维护成本高,每次新增一个表单,开发者都需要重复编写几乎相同的代码。如果需求或逻辑变更,必须在多个地方修改代码。状态管理和表单验证逻辑完全被分散在各个组件中,不同组件之间无法共享这些逻辑。

2、使用自定义 Hooks

将表单的状态管理和逻辑提取到一个自定义 Hook 中,可以极大地简化代码,并实现逻辑的复用。

我们开发一个 useForm 自定义 Hook,用于管理表单的输入值、以及逻辑等。下面是简化的实现代码:

import React, { useState } from 'react';

function useForm(initialValues) {
  const [values, setValues] = useState(initialValues);

  const handleChange = (event) => {
    setValues({ ...values, [event.target.name]: event.target.value });
  };

  return { values, handleChange };
}

使用场景:

我们可以在多个表单组件中复用 useForm,实现输入管理和逻辑的统一处理:

import React, { useState } from 'react';
import { useForm } form './component/customHooks'

function UserForm() {
  const { values, handleChange } = useForm({ name: '', workcode: '' });

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log('handleSubmit:', values);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="name"
        type="text"
        value={values.name}
        onChange={handleChange}
        placeholder="Name"
      />
      <input
        name="workcode"
        type="workcode"
        value={values.workcode}
        onChange={handleChange}
        placeholder="workcode"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

function AdminForm() {
  const { values, handleChange } = useForm({ name: '', workcode: '' });

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log('handleSubmit:', values);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="name"
        type="text"
        value={values.name}
        onChange={handleChange}
        placeholder="Name"
      />
      <input
        name="workcode"
        type="workcode"
        value={values.workcode}
        onChange={handleChange}
        placeholder="workcode"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

通过 useForm,我们可以轻松管理多个表单的输入状态和逻辑,避免在每个组件中重复编写这些代码。那么自定义 Hook 到底适用哪些适用场景呢?

自定义 Hook 的使用场景

1. 封装通用的 API 逻辑

项目中多个组件需要调用同一 API,并对结果进行状态管理。以往我们需要在每个组件内重复实现状态管理逻辑,而通过自定义 Hook,可以将 API 调用和状态管理逻辑抽离出来,让组件更专注于 UI 渲染。

// 封装通用的 API 请求逻辑
function useFetchData(apiUrl) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch(apiUrl);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    }
    fetchData();
  }, [apiUrl]);

  return { data, loading, error };
}

// 组件中使用自定义 Hook
function MyComponent() {
  const { data, loading, error } = useFetchData('/api/data');

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  return <div>{JSON.stringify(data)}</div>;
}

2. 处理副作用

某些组件需要在不同的生命周期阶段执行异步操作,比如页面加载时请求数据或窗口尺寸变化时调整布局。通过 useEffect 的组合,我们将这些逻辑独立封装在 Hook 中,从而减少组件中的代码。

// 封装窗口尺寸变化的逻辑
function useWindowSize() {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return windowSize;
}

// 组件中使用自定义 Hook
function LayoutComponent() {
  const { width, height } = useWindowSize();
  return <div>Window size: {width} x {height}</div>;
}

简洁高效,复用性还 Good~

总结

通过使用自定义 Hooks,能够有效解决 React 组件中的重复逻辑、状态管理和维护成本的问题。上述例子清晰地展示了从类组件到函数组件的转变,并通过自定义 Hooks 简化了表单处理的逻辑。对于项目,特别是需要频繁维护和扩展的项目,使用好自定义 Hooks 有着显著作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值