You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React初学者指南:继承公共Props与方法的最佳实践

实现 React 中类似抽象类的公共父组件方案

作为 React 开发者,我完全理解你想要复用公共逻辑、让子组件灵活定制的需求——这在 React 里可以通过两种主流方式实现,既可以贴近你熟悉的“抽象类”思路,也能遵循 React 推荐的组合模式。下面分两种场景详细说明:


一、类组件方案(贴近 Java 抽象类思路)

如果还在使用类组件,可以创建一个抽象基类组件,定义公共 Props、方法和基础渲染逻辑,子组件继承后按需重写或扩展。

1. 创建公共父组件(抽象基类)

这个组件只实现通用逻辑,留出“抽象方法”让子组件必须实现核心输入部分:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class BaseInput extends Component {
  // 定义公共Props的类型约束
  static propTypes = {
    label: PropTypes.string.isRequired,
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    onChange: PropTypes.func.isRequired,
    disabled: PropTypes.bool,
  };

  // 公共默认Props
  static defaultProps = {
    disabled: false,
  };

  // 公共方法:通用值格式化逻辑,子组件可重写
  formatValue = (value) => String(value);

  // 公共UI:渲染通用标签
  renderLabel = () => <label>{this.props.label}</label>;

  // 抽象方法:子组件必须实现的核心输入渲染
  renderInput = () => {
    throw new Error('子组件必须实现 renderInput 方法');
  };

  // 基础渲染框架
  render() {
    const { disabled } = this.props;
    return (
      <div className="input-wrapper" style={{ opacity: disabled ? 0.6 : 1 }}>
        {this.renderLabel()}
        {this.renderInput()}
      </div>
    );
  }
}

2. 实现数字输入子组件

继承基类后,你可以灵活添加专属 Props、重写默认值和公共方法:

import React from 'react';
import BaseInput from './BaseInput';
import PropTypes from 'prop-types';

class NumberInput extends BaseInput {
  // 扩展专属Props的类型约束
  static propTypes = {
    ...BaseInput.propTypes,
    min: PropTypes.number,
    max: PropTypes.number,
  };

  // 设置数字输入的专属默认值
  static defaultProps = {
    ...BaseInput.defaultProps,
    min: 0,
    max: 100,
  };

  // 重写格式化方法:只保留有效数字
  formatValue = (value) => {
    const num = parseInt(value, 10);
    return isNaN(num) ? this.props.min : Math.max(this.props.min, Math.min(this.props.max, num));
  };

  // 实现核心输入渲染
  renderInput = () => {
    const { value, onChange, disabled, min, max } = this.props;
    const handleChange = (e) => onChange(this.formatValue(e.target.value));

    return (
      <input
        type="number"
        value={value}
        onChange={handleChange}
        disabled={disabled}
        min={min}
        max={max}
      />
    );
  };
}

3. 实现日期输入子组件

同样的逻辑,日期输入可以忽略数字输入的 min/max Props,只保留公共部分并添加专属配置:

import React from 'react';
import BaseInput from './BaseInput';
import PropTypes from 'prop-types';

class DateInput extends BaseInput {
  // 只扩展日期专属Props
  static propTypes = {
    ...BaseInput.propTypes,
    format: PropTypes.string,
  };

  static defaultProps = {
    ...BaseInput.defaultProps,
    format: 'YYYY-MM-DD',
  };

  // 重写格式化方法:处理日期格式(示例简化,实际可搭配 date-fns/moment)
  formatValue = (value) => value;

  renderInput = () => {
    const { value, onChange, disabled } = this.props;
    return (
      <input
        type="date"
        value={value}
        onChange={(e) => onChange(e.target.value)}
        disabled={disabled}
      />
    );
  };
}

二、函数组件方案(React 现代推荐模式)

现在 React 更推荐组合而非继承,我们可以用「自定义 Hook 提取公共逻辑 + 公共 UI 组件承载布局」的方式,既实现复用,又保持子组件的灵活性。

1. 创建公共逻辑 Hook

提取输入组件的通用逻辑,子组件可以直接调用或重写:

import { useCallback } from 'react';

export const useBaseInput = (props) => {
  const { value, onChange, disabled } = props;

  // 通用格式化方法,子组件可覆盖
  const formatValue = useCallback((inputValue) => String(inputValue), []);

  // 通用变更处理逻辑
  const handleBaseChange = useCallback(
    (inputValue) => onChange(formatValue(inputValue)),
    [onChange, formatValue]
  );

  return { disabled, handleBaseChange, formatValue };
};

2. 创建公共布局组件

承载通用的标签、容器样式,子组件只需传入核心输入部分:

export const BaseInputLayout = ({ label, children, disabled }) => {
  return (
    <div className="input-wrapper" style={{ opacity: disabled ? 0.6 : 1 }}>
      <label>{label}</label>
      {children}
    </div>
  );
};

3. 实现数字输入子组件

组合公共 Hook 和布局组件,灵活定制专属逻辑:

import { useCallback } from 'react';
import { useBaseInput, BaseInputLayout } from './BaseInput';

export const NumberInput = ({ label, value, onChange, disabled, min = 0, max = 100 }) => {
  const { disabled: baseDisabled } = useBaseInput({ value, onChange, disabled });

  // 自定义数字格式化逻辑
  const formatNumber = useCallback(
    (inputValue) => {
      const num = parseInt(inputValue, 10);
      return isNaN(num) ? min : Math.max(min, Math.min(max, num));
    },
    [min, max]
  );

  const handleChange = useCallback(
    (e) => onChange(formatNumber(e.target.value)),
    [onChange, formatNumber]
  );

  return (
    <BaseInputLayout label={label} disabled={baseDisabled}>
      <input
        type="number"
        value={value}
        onChange={handleChange}
        disabled={baseDisabled}
        min={min}
        max={max}
      />
    </BaseInputLayout>
  );
};

核心优势总结

  • 类组件方案:贴近面向对象的抽象类思路,适合习惯继承模式的开发者,但要注意避免组件过度耦合。
  • 函数组件方案:符合 React 现代设计理念,通过 Hook 提取逻辑、组合组件实现复用,灵活性更高,是官方推荐的方式。
  • Props 灵活性:两种方案都支持子组件自由扩展、重写默认值或忽略不需要的公共 Props,完全满足你的需求。

内容的提问来源于stack exchange,提问作者Rahul Bansal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:19:41