npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dynamicformdjx-react

v0.3.1

Published

Dynamic form for React

Downloads

241

Readme

dynamicformdjx-react

基于 React 的动态表单及录入。

Document

Vue3 版本

Vue2 版本

概述

DynamicForm 一个灵活且动态的表单组件,使用数组,简化模版操作,提供多种hook快速操作表单等。

  • 简化节点代码,快速处理表单
  • 提供render2函数渲染表单项,使用函数渲染值或自定义Component函数
  • 提供多种hooks函数,快速处理数据值

DynamicInput 组件是一个灵活且动态的表单输入组件,允许用户添加、修改和删除键值对。它提供了多种自定义选项,如按钮文本、表单布局和输入过滤

  • 支持通过 v-model 双向绑定任意对象,(包含受控和非受控) 可动态增删字段
  • 支持将值解析为:字符串 / 数字 / 数组(字符串数组、数字数组)
  • 文案、样式、数组分隔符等均可配置

安装

# 任意一种
npm install dynamicformdjx-react
# or
yarn add dynamicformdjx-react
# or
pnpm add dynamicformdjx-react

动态表单

与Antd配合

需配合antd v5+ 版本以上

简单表单
import {useRef, useState} from "react";
import {Button, Input, Radio} from "antd";
import {AdDynamicForm, type adDynamicFormRef, renderInput} from "dynamicformdjx-react/antd";
import {omitFormCommonKey, OmitValue, useDyForm, useReactiveForm} from "dynamicformdjx-react";
import type {PresetType} from "dynamicformdjx-react/types";
import type {Rule} from "antd/es/form";

type RowProps = {
    username: string
    password: string
    desc: string
    preset: string
}
const SimpleForm = () => {
    const [presetType, setPresetType] = useState<PresetType>('fullRow')
    const [formItems, setFormItems] = useReactiveForm<RowProps, Rule | Rule[]>([
        {
            key: "username",
            label: "用户名",
            value: "",
            allowClear: true,
            rule: [{required: true, message: 'Please input your username!', validateTrigger: 'onBlur'}],
            render2: f => renderInput({}, f),
            span: 12
        },
        {
            key: "password",
            label: "密码",
            required: true,
            value: "",
            render2: (f) => <Input.Password placeholder="请输入密码" {...OmitValue(f, omitFormCommonKey)}/>,
            span: 8,
            offset: 2,
            sort: 0
        },
        {
            key: "preset",
            label: "表格预设",
            value: "fullRow",
            render2: (f) => <Radio.Group
                value={f.value}
                options={[
                    {value: 'fullRow', label: 'row'},
                    {value: 'grid', label: 'grid'},
                ]}
                onChange={(v) => {
                    setPresetType(v.target.value)
                }}
            />,
        }
    ])
    const useForm = useDyForm([formItems, setFormItems])
    const antdFormRef = useRef<adDynamicFormRef>(null)
    const rules: Partial<Record<keyof RowProps, Rule | Rule[]>> = {
        desc: [{required: true, message: '请输入详情'}]
    }
    return (
        <div className='dynamicFormTest'>
            <AdDynamicForm ref={antdFormRef} rules={rules} validateTrigger={null} items={formItems}
                           preset={presetType}/>
            <div className="footer" style={{
                display: 'flex',
                gap: '5px'
            }}>
                <Button color={'green'} variant={'outlined'} onClick={() => {
                    // const res=antdFormRef.current?.getResult?.()
                    const res = useForm.getValues()
                    console.log(res)
                }}>getData</Button>
                <Button color={'orange'} variant={'outlined'} onClick={() => {
                    useForm.setValues({
                        username: 'antd',
                        password: 'I love you'
                    })
                }}>setData</Button>
                <Button color={'blue'} variant={'outlined'} onClick={() => {
                    antdFormRef.current?.validator().then(v => {
                        console.log(v)
                    }).catch(r => {
                        console.log(r)
                    })
                }}>validator</Button>
                <Button color={'red'} variant={'outlined'} onClick={() => {
                    useForm.onReset()
                }}>reset</Button>
                <Button variant={'outlined'} onClick={() => {
                    useForm.setDisabled(true)
                }}>setDisabled</Button>
            </div>
        </div>
    );
};

export default SimpleForm;
自定义表单
import {useRef} from "react";
import {Button, Input, Select} from "antd";
import {
    DynamicInput,
    type dynamicInputRef,
    omitFormCommonKey,
    OmitValue,
    useDyForm,
    useReactiveForm
} from "dynamicformdjx-react";
import {AdDynamicForm, type adDynamicFormRef} from "dynamicformdjx-react/antd";
import type {Rule} from "antd/es/form";

type RowProps = {
    username: string
    job: string
    json: object
}
const CustomForm = () => {
    const [formItems, setFormItems] = useReactiveForm<RowProps, Rule | Rule[]>([
        {
            key: "username",
            label: "用户名",
            value: "",
            allowClear: true,
            render2: (f) => <Input placeholder="请输入姓名" {...OmitValue(f, omitFormCommonKey)}/>,
            rule: [
                {
                    required: true,
                    message: 'Please confirm your username!',
                },
                {
                    validator: async (_, value) => {
                        if (!value) return; // 交给 required 处理
                        if (value.length < 3) {
                            throw new Error('至少 3 个字符');
                        }
                    },
                }
            ],
        },
        {
            key: "job",
            label: "职位",
            value: "",
            required: true,
            render2: (f) => <Select
                style={{
                    width: '100%'
                }}
                options={[
                    {value: 'jack', label: 'Jack'},
                    {value: 'lucy', label: 'Lucy'},
                    {value: 'Yiminghe', label: 'yiminghe'},
                    {value: 'disabled', label: 'Disabled', disabled: true},
                ]}
            />,
        },
        {
            key: "json",
            label: "Json",
            value: {},
            isCustom: true,
            rule: [
                {
                    required: true,
                    message: 'json 不能为空'
                },
                {
                    validator: async (_, value) => {
                        if (!value || Object.keys(value).length === 0) {
                            throw new Error('json 不能为空');
                        }
                    },
                }
            ],
            render2: f => {
                return <DynamicInput ref={dynamicInputRef} value={f.value} onChange={(v: object) => {
                    f.value = v
                }} isController/>
            },
        },
    ])
    const useForm = useDyForm([formItems, setFormItems])
    const antdFormRef = useRef<adDynamicFormRef>(null)
    const dynamicInputRef = useRef<dynamicInputRef>(null)
    return (
        <div className='dynamicFormTest'>
            <AdDynamicForm ref={antdFormRef} items={formItems}/>
            <div className="footer" style={{
                display: 'flex',
                gap: '5px'
            }}>
                <Button color={'green'} variant={'outlined'} onClick={() => {
                    // const res=antdFormRef.current?.getResult?.()
                    const res = useForm.getValues()
                    console.log(res)
                }}>getData</Button>
                <Button color={'orange'} variant={'outlined'} onClick={() => {
                    useForm.setValues({
                        username: 'antd',
                        job: 'jack'
                    })
                    dynamicInputRef.current?.onSet?.({
                        a: 'Hello world',
                        b: 1314,
                        c: [5, 2, 0]
                    })
                }}>setData</Button>
                <Button color={'blue'} variant={'outlined'} onClick={() => {
                    antdFormRef.current?.validator().then(v => {
                        console.log(v)
                    }).catch(r => {
                        console.error(r)
                    })
                }}>validator</Button>
                <Button color={'red'} variant={'outlined'} onClick={() => {
                    useForm.onReset()
                    dynamicInputRef.current?.onSet?.({})
                }}>reset</Button>
            </div>
        </div>
    );
};

export default CustomForm;
装饰表单
import {DATETIME_FORMAT, TIME_FORMAT, useDyForm} from "dynamicformdjx-react";
import type {Rule} from "antd/es/form";
import {
    AdDynamicForm,
    type adDynamicFormRef, useDecorateForm,
    datePickerFormat, renderDatePicker
} from "dynamicformdjx-react/antd";
import {useRef} from "react";
import {Button} from "antd";

type FormRow = {
    password: string
    job: number
    birthday: string
    time: string
}
const DecorateForm = () => {
    const [formItems, setFormItems] = useDecorateForm<FormRow, Rule | Rule[]>([
        {
            key: "password",
            label: "密码",
            value: null,
            allowClear: true,
            placeholder: '请输入密码',
            required: true,
            type: 'password',
            renderType: 'renderInput'
        },
        {
            key: "job",
            label: "职位",
            value: null,
            allowClear: true,
            options: ['前端', '后端'].map((label, value) => ({label, value})),
            renderType: 'renderSelect',
        },
        {
            key: "birthday",
            label: "生日",
            value: null,
            // render2: f => renderDatePicker({type: 'datetime', showTime: true}, f),
            renderType: 'renderDatePicker',
            renderProps: {
                type: 'datetime', showTime: true, isRange: true
            },
            formItemProps: {
                ...datePickerFormat({formatStr: DATETIME_FORMAT})
            }
        },
        {
            key: "time",
            label: "时间",
            value: ['00:00:00', '23:59:00'],
            renderType: 'renderDatePicker',
            renderProps: {
                isRange: true,
            },
            formItemProps: {
                ...datePickerFormat({formatStr: TIME_FORMAT})
            }
        }
    ])
    const useForm = useDyForm([formItems, setFormItems])
    const antdFormRef = useRef<adDynamicFormRef>(null)
    return (
        <div className='dynamicFormTest'>
            <AdDynamicForm ref={antdFormRef} items={formItems}/>
            <div className="footer" style={{
                display: 'flex',
                gap: '5px'
            }}>
                <Button color={'green'} variant={'outlined'} onClick={() => {
                    // const res=antdFormRef.current?.getResult?.()
                    const res = useForm.getValues()
                    console.log(res)
                }}>getData</Button>
                <Button color={'orange'} variant={'outlined'} onClick={() => {
                    useForm.setValues({
                        password: 'Antd',
                        job: 0,
                        birthday: '2026-02-11'
                    })
                }}>setData</Button>
                <Button color={'blue'} variant={'outlined'} onClick={() => {
                    antdFormRef.current?.validator().then(v => {
                        console.log(v)
                    }).catch(r => {
                        console.log(r)
                    })
                }}>validator</Button>
                <Button color={'red'} variant={'outlined'} onClick={() => {
                    useForm.onReset()
                }}>reset</Button>
                <Button variant={'outlined'} onClick={() => {
                    useForm.setDisabled(true)
                }}>setDisabled</Button>
            </div>
        </div>
    );
}
export default DecorateForm;
总表单
import {useRef, useState} from "react";
import {Button, Input, Radio} from "antd";
import {
    AdDynamicForm,
    type adDynamicFormRef, renderCheckbox, renderCheckboxGroup, renderDatePicker, renderDynamicTags,
    renderInput, renderInputNumber,
    renderPopSelect, renderRadioButtonGroup, renderRadioGroup,
    renderSelect, renderSlider, renderSwitch, renderTimePicker,
    renderTreeSelect
} from "dynamicformdjx-react/antd";
import {useDyForm, useReactiveForm} from "dynamicformdjx-react";
import type {Rule} from "antd/es/form";

type RowProps = {
    username: string
    password: string
    gender: number
    description: string
    email: string
    birthday: string
    desc: string
    sex: number
    birthdayT: number
    admin: number
    favorite: number[]
    job: number
    job2: number
    job3: number
    checkbox: boolean
    future: string[]
    slider: number
    inputNumber: number
}
const AllForm = () => {
    const [formItems, setFormItems] = useReactiveForm<RowProps, Rule | Rule[]>([
        {
            key: "username",
            label: "用户名",
            value: "",
            allowClear: true,
            render2: f => renderInput({}, f),
        },
        {
            key: "password",
            label: "密码",
            required: true,
            value: "",
            render2: (f) => renderInput({}, {...f, type: 'password'}),
        },
        {
            key: "gender",
            label: "性别",
            value: null,
            placeholder: '请选择性别',
            labelField: 'f',
            valueField: 'v',
            showSearch: true,
            allowClear: true,
            searchOnLabel: true,
            options: [
                {f: <b>男</b>, v: 0},
                {f: '女', v: 1}
            ],
            render2: (f) => renderSelect([], {}, f)
        },
        {
            key: "job",
            label: "职业",
            value: null,
            placeholder: '请选择职业',
            labelField: 'f',
            valueField: 'v',
            showSearch: true,
            allowClear: true,
            searchOnLabel: true,
            childField: 'childOptions',
            options: [
                {
                    f: '前端', v: '1', childOptions: [
                        {f: '网页开发', v: '1-1'},
                        {f: '小程序开发', v: '1-2'},
                    ]
                },
                {
                    f: '后端', v: '2', childOptions: [
                        {f: '后台开发', v: '2-1'},
                        {f: '运维', v: '2-2'},
                    ]
                }
            ],
            render2: (f) => renderTreeSelect([], {
                treeDefaultExpandAll: true
            }, f),
        },
        {
            key: "job2",
            label: "职位2",
            value: null,
            labelField: 'l',
            valueField: 'v',
            options: ['Drive My Car', 'Norwegian Wood'].map((label, index) => ({
                l: label,
                v: label,
                children: [
                    {l: 'aaa' + index, v: 'aaa' + index},
                    {l: 'bbb' + index, v: 'bbb' + index},
                ]
            })),
            // mode: 'multiple',
            render2: f => renderPopSelect([], {}, f),
        },
        {
            key: "sex",
            label: "性别",
            labelField: 'label1',
            valueField: 'value1',
            value: null,
            options: [
                {label1: '男', value1: 0}, {label1: '女', value1: 1},
            ],
            render2: f => renderRadioGroup([], {}, f),
        },
        {
            key: "favorite",
            label: "爱好",
            labelField: 'fl',
            valueField: 'fv',
            sort: 1,
            options: [
                {fl: '吃饭', fv: 0},
                {fl: '睡觉', fv: 1},
                {fl: '打豆豆', fv: 2},
            ],
            value: [],
            render2: f => renderCheckboxGroup([], {}, f),
        },
        {
            key: "admin",
            label: "管理员?",
            value: null,
            render2: f => renderSwitch({}, f),
        },
        {
            key: "birthday",
            label: "生日",
            value: null,
            render2: f => renderDatePicker({showTime: true}, f),
        },
        {
            key: "birthdayT",
            label: "时间",
            value: null,
            render2: f => renderTimePicker({}, f),
        },
        {
            key: "future",
            label: "未来",
            value: [
                {label: '你没见过不等于没有', value: 'hello world 1'},
                {
                    label: '不要给自己设限',
                    value: 'hello world 2'
                },
                {
                    label: '不要说连升两级',
                    value: 'hello world 3'
                },
                {
                    label: '直接升到 CEO 都是有可能的',
                    value: 'hello world 4'
                }
            ],
            render2: f => renderDynamicTags(f.value, {}, f),
        },
        {
            key: "checkbox",
            label: "复选",
            value: true,
            render2: f => renderCheckbox({}, f),
            formItemProps: {
                valuePropName: 'checked',
            }
        },
        {
            key: "slider",
            label: "滑块",
            value: 0,
            render2: f => renderSlider({}, f),
        },
        {
            key: "inputNumber",
            label: "数字输入",
            value: 20,
            render2: f => renderInputNumber({}, f),
        },
    ])
    const useForm = useDyForm([formItems, setFormItems])
    const antdFormRef = useRef<adDynamicFormRef>(null)
    const rules: Partial<Record<keyof RowProps, Rule | Rule[]>> = {
        desc: [{required: true, message: '请输入详情'}]
    }
    return (
        <div className='dynamicFormTest'>
            <AdDynamicForm ref={antdFormRef} rules={rules} items={formItems}/>
            <div className="footer" style={{
                display: 'flex',
                gap: '5px'
            }}>
                <Button color={'green'} variant={'outlined'} onClick={() => {
                    // const res=antdFormRef.current?.getResult?.()
                    const res = useForm.getValues()
                    console.log(res)
                }}>getData</Button>
                <Button color={'orange'} variant={'outlined'} onClick={() => {
                    useForm.setValues({
                        username: 'antd',
                        password: 'I love you'
                    })
                }}>setData</Button>
                <Button color={'blue'} variant={'outlined'} onClick={() => {
                    antdFormRef.current?.validator().then(v => {
                        console.log(v)
                    }).catch(r => {
                        console.log(r)
                    })
                }}>validator</Button>
                <Button color={'red'} variant={'outlined'} onClick={() => {
                    useForm.onReset()
                }}>reset</Button>
                <Button variant={'outlined'} onClick={() => {
                    useForm.setDisabled(true)
                }}>setDisabled</Button>
            </div>
        </div>
    );
};

export default AllForm;

动态录入

此录入无需组件库依赖

1.单组件

import {useState} from "react";
import {DynamicInput, dynamicFormRef} from "dynamicformdjx-react";

function App() {
    const [obj, setObj] = useState<Record<string, any>>({
        a: 'Hello world',
        b: 1314,
        c: [5, 2, 0]
    });
    const dynamicInputRef = useRef<dynamicFormRef>(null)
    return (<div>
        <DynamicInput ref={dynamicInputRef} isController value={obj} onChange={(e) => setObj(e)}/>
        <pre>
            {JSON.stringify(obj, null, 2)}
        </pre>
        <div>
            <button onClick={() => {
                dynamicInputRef.current?.onSet?.({
                    test: 'hello World'
                })
            }}>setData
            </button>
        </div>
    </div>)
}

export default App

2.级联基本使用

import {useState} from "react";
import {DynamicCascadeInput, dynamicCascadeInputRef} from "dynamicformdjx-react";

const App = () => {
    const [obj, setObj] = useState<Record<string, any>>({
        a: {
            b: {
                c: {
                    d: {
                        e: "hello world"
                    }
                }
            }
        },
        aa: [5, 2, 0],
        aaa: 1314
    });
    const dynamicInputRef = useRef<dynamicCascadeInputRef>(null)
    return (<div>
        <DynamicCascadeInput ref={dynamicInputRef} isController value={obj} onChange={(e) => setObj(e)}/>
        <pre>
            {JSON.stringify(obj, null, 2)}
        </pre>
        <div>
            <button onClick={() => {
                dynamicInputRef.current?.onSet?.({
                    test: 'hello world'
                })
            }}>setData
            </button>
        </div>
    </div>)
}
export default App;