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

基于Formik框架实现增减按钮组并改造TextInputField及验证

扩展Formik表单实现带增减按钮的数值输入组件

需求概述

需要为Formik表单框架添加一个支持增减操作的数值输入组件,要求:

  • 整合现有的增减按钮组功能
  • 允许直接输入数值,替代原有的只读按钮展示
  • 实现数值范围验证(限制在指定最大/最小值内)
  • 适配现有的TextInputField组件结构

现有代码参考

增减按钮组组件

import React from "react";
import Button from "@material-ui/core/Button";
import ButtonGroup from "@material-ui/core/ButtonGroup";

class GroupedButtons extends React.Component {
  state = { counter: 0 };

  handleIncrement = () => {
    this.setState((state) => ({ counter: state.counter + 1 }));
  };

  handleDecrement = () => {
    this.setState((state) => ({ counter: state.counter - 1 }));
  };
  render() {
    return (
      <ButtonGroup size="small" aria-label="small outlined button group">
        <Button onClick={this.handleDecrement}>-</Button>
        <Button disabled>{this.state.counter}</Button>
        <Button onClick={this.handleIncrement}>+</Button>
      </ButtonGroup>
    );
  }
}

export default GroupedButtons;

待改写的TextInputField组件

import React, { Component } from 'react';
import TextField from '@mui/material/TextField';
import InputAdornment from '@mui/material/InputAdornment';

class TextInputField extends Component {
    constructor(props, context) {
      super(props, context);
    }

    render() {
      return (
            <TextField
              fullWidth={true}
              type={this.props.item.type}
              label={this.props.item.label}
              placeholder={this.props.item.placeholder}
              InputLabelProps={this.props.item.placeholder? {shrink: true} : {}}
              InputProps={{
                startAdornment: this.props.item.startAdornment? <InputAdornment position="start">{this.props.item.startAdornment}</InputAdornment>: null,
                endAdornment: this.props.item.endAdornment? <InputAdornment position="end">{this.props.item.endAdornment}</InputAdornment>: null
              }}
              inputProps={{
                maxLength: this.props.item.charLimit? this.props.item.charLimit:null,
                autoComplete: this.props.item.autoComplete? this.props.item.autoComplete:"off"
              }}
              rows={(this.props.item.type === "comment") ? 6 : null}
              multiline={(this.props.item.type === "comment") ? true : false}
              disabled={this.props.item.disabled}
              {...this.props.field}
              onChange={event => {
                this.props.form.setFieldValue(this.props.name, event.target.value);
                this.props.onHandle(this.props.name, event.target.value);
              }}
              error={this.props.meta.touched && (this.props.meta.error && this.props.meta.error.length > 0 ? true : false)}
            />
      )
    }
}

export default TextInputField

改写后的实现方案

下面是整合了增减按钮、直接输入和范围验证的Formik适配组件:

import React, { Component } from 'react';
import TextField from '@mui/material/TextField';
import InputAdornment from '@mui/material/InputAdornment';
import Button from "@material-ui/core/Button";
import ButtonGroup from "@material-ui/core/ButtonGroup";

class NumericInputField extends Component {
    // 处理数值合法性校验
    validateValue = (value) => {
        const { min = 0, max = Infinity } = this.props.item;
        let numValue = parseInt(value);
        
        // 空值或非数字设为最小值
        if (isNaN(numValue)) {
            numValue = min;
        }
        // 限制在最大最小值范围内
        numValue = Math.max(min, Math.min(max, numValue));
        
        return numValue.toString();
    };

    // 增减操作处理
    handleChangeCount = (step) => {
        const { form, name } = this.props;
        const currentValue = parseInt(form.values[name]) || this.props.item.min || 0;
        const newValue = currentValue + step;
        const validatedValue = this.validateValue(newValue);
        
        form.setFieldValue(name, validatedValue);
        this.props.onHandle(name, validatedValue);
    };

    // 输入框值变化处理
    handleInputChange = (event) => {
        const { form, name } = this.props;
        const validatedValue = this.validateValue(event.target.value);
        
        form.setFieldValue(name, validatedValue);
        this.props.onHandle(name, validatedValue);
    };

    render() {
        const { item, field, meta } = this.props;
        // 仅当类型为数值时显示增减按钮
        const isNumericType = item.type === 'number';
        
        return (
            <TextField
                fullWidth={true}
                type={isNumericType ? 'number' : item.type}
                label={item.label}
                placeholder={item.placeholder}
                InputLabelProps={item.placeholder ? { shrink: true } : {}}
                InputProps={{
                    startAdornment: item.startAdornment ? 
                        <InputAdornment position="start">{item.startAdornment}</InputAdornment> : null,
                    endAdornment: isNumericType ? (
                        <InputAdornment position="end">
                            <ButtonGroup size="small" aria-label="numeric adjust button group">
                                <Button 
                                    onClick={() => this.handleChangeCount(-1)}
                                    disabled={parseInt(field.value) <= (item.min || 0)}
                                >-</Button>
                                <Button 
                                    onClick={() => this.handleChangeCount(1)}
                                    disabled={parseInt(field.value) >= (item.max || Infinity)}
                                >+</Button>
                            </ButtonGroup>
                        </InputAdornment>
                    ) : (
                        item.endAdornment ? <InputAdornment position="end">{item.endAdornment}</InputAdornment> : null
                    )
                }}
                inputProps={{
                    maxLength: item.charLimit ? item.charLimit : null,
                    autoComplete: item.autoComplete ? item.autoComplete : "off",
                    min: item.min,
                    max: item.max
                }}
                rows={(item.type === "comment") ? 6 : null}
                multiline={(item.type === "comment") ? true : false}
                disabled={item.disabled}
                {...field}
                onChange={this.handleInputChange}
                error={meta.touched && !!meta.error}
                helperText={meta.touched && meta.error}
            />
        );
    }
}

export default NumericInputField;

关键改进点

  1. 整合增减按钮:通过InputAdornment将按钮组添加到输入框右侧,仅当字段类型为number时显示
  2. 数值验证:
    • 输入非数字时自动修正为最小值
    • 限制数值在item.min和item.max范围内
    • 增减按钮在达到边界时自动禁用
  3. Formik适配:直接调用Formik的setFieldValue更新表单状态,同时触发外部onHandle回调
  4. 向后兼容:保留原TextInputField的所有功能,仅对数值类型字段增强

内容的提问来源于stack exchange,提问作者The Old County

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 02:00:55