react中通过配置项的写法实现受控表单组件

在日常项目开发中,有时不可避免的会遇到一些表单功能,个人觉得antd中的表单组件不是那么好用,有时候的取值很受限制,例如select选择框选中后想传出一个对象,而并非是value值,又或者日期选择框想传出一个字符串格式的日期或是时间戳格式的,如果是通过antd表单实现是非常受限制的,我这里介绍一下我常用的写法。

简单思路:我们把一个表单进行拆解,把每个input框拆解成一个组件,一个表单是由多个input组件构成的,通过配置项的形式,写一个配置项数组,数组里面写想要渲染的组件参数,然后循环遍历出input组件,实现一个表单。

表单代码

import React, {forwardRef, useImperativeHandle, useState} from 'react';
import {Modal} from "antd";

//这里是表单的配置项,也可以写在useState状态里
const config=[
    {
        componentType: "TextInput", // 组件类型
        name: "name",
        value: "information.name", // 值所在位置
        label: "姓名", // 标签名
        placeholder: "请输入姓名", // 空输入框提示
        message: "请输入姓名!", // 校验提示
        disabled: false, // 是否禁用
        required:true,//是否必填
        width: 200,
        span: 2,
        offset: 6,
    },
    {
        componentType: "TextInput", // 组件类型
        name: "phone",
        value: "information.phone", // 值所在位置
        label: "电话", // 标签名
        placeholder: "请输入电话", // 空输入框提示
        message: "请输入电话", // 校验提示
        disabled: false, // 是否禁用
        required:true,//是否必填
        width: 200,
        span: 2,
        offset: 6,
    },
    {
        componentType: "SelectInput", // 组件类型
        name: "address",
        value: "information.address", // 值所在位置
        label: "地址", // 标签名
        placeholder: "请选择地址", // 空输入框提示
        message: "请选择地址", // 校验提示
        disabled: false, // 是否禁用
        required:true,//是否必填
        width: 200,
        span: 2,
        offset: 6,
    },
    {
        componentType: "TimeInput", // 组件类型
        name: "time",
        value: "information.time", // 值所在位置
        label: "日期", // 标签名
        placeholder: "请选择日期", // 空输入框提示
        message: "请选择日期", // 校验提示
        disabled: false, // 是否禁用
        required:true,//是否必填
        width: 200,
        span: 2,
        offset: 6,
    },
]
const DemoModal = forwardRef((props,ref) => {
    const [visible,setVisible] = useState(false);
    const [information,setInformation] = useState({
        name:'',
        phone:'',
        address:'',
        time:''
    })
    //抛出isShow方法
    useImperativeHandle(ref,()=>({
        isShow(){
            setVisible(!visible)
        }
    }));

    const renderItem=(componentItem,index)=>{
        let ItemComponent = null;
        try {
            ItemComponent = require(`./InputComponents/${componentItem.componentType}.js`).default;
        } catch (e) {
            console.log(e);
        }
        let itemValue = eval(componentItem.value);
        let callBack = (val, name) => {
            let newInformation = { ...information };
            newInformation[name] = val;
            setInformation(newInformation);
        };

        return (
            <ItemComponent
                key={index}
                {...componentItem}
                itemValue={itemValue}
                callBack={callBack}
            ></ItemComponent>
        );
    }

    return (
        <Modal
            title={'表单demo'}
            open={visible}
            onOk={()=>{
                setVisible(false);
                console.log(information,'information')
            }}
            onCancel={()=>{setVisible(false)}}
        >
            <div>
                {config.map((item,index)=>{
                    return <div>{renderItem(item,index)}</div>
                })}
            </div>
        </Modal>
    );
});

export default DemoModal;

上面的代码是利用配置项的写法实现的受控表单;config数组对象中的componentType字段是对应的输入框组件名称,不要写错,value字段就是表单存储的数据位置,name是数据对应的key值。

这里表单的实现流程,通过遍历config配置项数组,调用renderItem方法,传入每一个配置项,一个配置项就是一个输入框组件的参数,通过require方法将组件引入,这里的require方法是nodejs中的方法;然后通过js中的eval方法将value的字符串转换为表达式(eval的使用方法JavaScript eval() 函数 | 菜鸟教程 (runoob.com)),并且创建一个callBack方法一并传入给组件,组件通过触发callBack方法,将数据返回。

下面是三种常用的表单输入框组件,输入框、选择框以及时间选择器,我这里统一进行了封装;如TextArea、数字输入框以及单选框、复选框等都可以参照下面的代码。

在项目里创建一个文件夹,文件夹下放封装好的input组件框。

在这些小组件中,如果有的表单需要数据回显,那么就把value属性添加上,然后value的值通过props.itemValue获取,就可以实现数据回显。

文本输入框组件代码

import { Form, Input } from "antd";
import { useEffect, useState } from "react";

export default function TextInput(props) {
    const [value, setValue] = useState(props.itemValue);
    return (
        <Form.Item
            label={props.label}
            labelCol={{span: props.span, offset: props.offset}}
            labelAlign={"left"}
        >
            <Input
                placeholder={props.placeholder}
                disabled={props.disabled}
                value={props.itemValue}
                name={props.name}
                style={{width: props.width}}
                onChange={(e) => {
                    let inputValue = e.target.value;
                    let name = props.name;
                    setValue(() => inputValue);
                    props.callBack && props.callBack(inputValue, name);
                }}
            ></Input>
        </Form.Item>
    );
}

选择框组件代码

import {Form, Select} from "antd";
import {useEffect, useState} from "react";

export default function SelectInput(props) {
    const [value, setValue] = useState(props.itemValue || "");
    return (
        <Form.Item
            label={props.label}
            labelCol={{span: props.span, offset: props.offset}}
            labelAlign={"left"}
        >
            <Select
                placeholder={props.placeholder}
                options={[
                    {
                        value: 'beijing',
                        label: '北京',
                        id: '1'
                    },
                    {
                        value: 'shanghai',
                        label: '上海',
                        id: '2'
                    },
                    {
                        value: 'hangzhou',
                        label: '杭州',
                        id: '3'
                    }
                ]}
                style={{width: props.width}}
                value={props.itemValue?props.itemValue:null}
                onChange={(value, option) => {
                    let name = props.name;
                    setValue(() => option);
                    props.callBack && props.callBack(option, name);
                }}
            ></Select>
        </Form.Item>
    );
}

select框里面的数据我在demo里是写死的,一般来说这里的options数据是需要后端返给我们的,我们可以在配置项里加上对应请求来的数据,然后同样的方法,通过eval转换成表达式,传递给select组件。写法如下:

日期选择框组件

import { DatePicker, Form } from "antd";
import moment from "moment";
import { useEffect, useState } from "react";

export default function TimeInput(props) {
    const [value, setValue] = useState(props.itemValue);
    return (
        <Form.Item
            label={props.label}
            labelCol={{ span: props.span, offset: props.offset }}
            labelAlign={"left"}
        >
            <DatePicker
                placeholder={props.placeholder}
                style={{ width: props.width }}
                disabled={props.disabled}
                name={props.name}
                format={"YYYY-MM-DD"}
                // value={props.itemValue ? moment(props.itemValue) : ""}
                allowClear={true}
                onChange={(date, dateString) => {
                    console.log(moment(date).format('YYYY-MM-DD HH:mm:ss'));
                    let dateTime = date ? moment(date).format('YYYY-MM-DD HH:mm:ss') : "";
                    let name = props.name;
                    setValue(() => dateTime);
                    props.callBack && props.callBack(dateTime, name);
                }}
            />
        </Form.Item>
    );
}

 按照以上的写法,就可以实现一个受控的表单,有多少个输入框,对应的配置项就写多少个就可以了。

 

可以看到点击ok后,打印的information对象中字段的数据都拿到了,而且select传过来了一个对象,并不是value,time也是预期的格式。

如果有更好的方法和建议,欢迎分享评论沟通。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值