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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@uiw/react-form

v4.22.3

Published

Form component

Downloads

801

Readme

Form 表单

Buy me a coffee Open in unpkg NPM Downloads npm version

由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据。

import { Form, FormItem } from 'uiw';
// or
import { Form, FormItem } from '@uiw/react-form';

基本用法

import React, { useState, useRef } from "react";
import { Form, Input, Row, Col, Slider, Button, Notify } from 'uiw';

export default function Demo() {
  const form = useRef()

  const onSubmit = () => {
    form.current.onSubmit()
  }
  const resetForm = () => {
    form.current.resetForm()
  }
  const getFieldValues = () => {
    console.log('getFieldValues', form.current.getFieldValues())
  }

  const setFieldValue=()=>{
    form.current.setFieldValue('name','UIW')
  }

  return (
    <div>
      <Form
        ref={form}
        onChange={({ initial, current }) => {
          console.log('onChange', initial, current);
        }}
        onSubmit={({ initial, current }) => {
          if (current.name === initial.name) {
            Notify.error({
              title: '提交失败!',
              description: `表单提交内容为空!`,
            });
          } else {
            Notify.success({
              title: '提交成功!',
              description: `姓名为:${current.name},提交完成,将自动填充初始化值!`,
            });
          }
        }}
        fields={{
          name: {
            label: '姓名',
            children: <Input placeholder="请输入姓名" />
          },
        }}
      >
        {({ fields, state, canSubmit }) => {
          return (
            <div>
              <Row>
                <Col style={{ maxWidth: 300 }}>{fields.name}</Col>
              </Row>
              <Row>
                <Col>
                  <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                </Col>
              </Row>
              <Row>
                <Col>
                  {JSON.stringify(state.current)}
                </Col>
              </Row>
            </div>
          )
        }}
      </Form>
      <Button type="primary" onClick={onSubmit} >submit</Button>
      <Button type="primary" onClick={resetForm}>resetForm</Button>
      <Button type="primary" onClick={getFieldValues}>getValues</Button>
      <Button type="primary" onClick={setFieldValue}>setFieldValue</Button>
    </div>
  )
}

自定义校验

一般校验可不需引入外部包解决,如果遇到大型工程表单比较多的地方推荐使用 jquense/yup

import React from 'react';
import { Form, Input, Notify, Checkbox, Switch, RadioGroup, Radio, Textarea, Row, Col, Button, Select, SearchSelect } from 'uiw';

export default function Demo() {
  return(
    <Form
      onSubmit={({initial, current}) => {
        const errorObj = {};
        if (current.userName.startsWith('u')) {
          errorObj.userName = `姓名 ${current.userName} 不能以 ‘u’ 开头`;
        }
        if (!current.checkboxOne) {
          errorObj.checkboxOne = '一个多选条件 为必填';
        }
        if (!current.terms) {
          errorObj.terms = '必须统一服务条款';
        }
        if(Object.keys(errorObj).length > 0) {
          const err = new Error();
          err.filed = errorObj;
          throw err;
        }
        Notify.success({
          title: '提交成功!',
          description: `姓名为:${current.userName},提交完成,将自动填充初始化值!`,
        });
      }}
      onSubmitError={(error) => {
        if (error.filed) {
          return { ...error.filed };
        }
        return null;
      }}
      fields={{
        userName: {
          initialValue: 'uiw',
          label: '姓名',
          children: <Input type="text" />,
          help: '以“u”开头的名字将在此处显示错误信息'
        },
        age: {
          initialValue: '9',
          label: '年龄',
          children: <Input type="number" />
        },
        select: {
          children: (
            <Select>
              <Select.Option value="w">Choose an item...</Select.Option>
              <Select.Option value="1">One</Select.Option>
              <Select.Option value="2">Two</Select.Option>
              <Select.Option value="3">Three</Select.Option>
              <Select.Option value="4">Four</Select.Option>
            </Select>
          )
        },
        searchSelect: {
          initialValue:[{label: 'a7', value: 7},{label: 'a8', value: 8}],
          children: (
            <SearchSelect
              allowClear
              labelInValue={true}
              showSearch={true}
              mode="multiple"
              disabled={false}
              placeholder="请选择选项"
              option={[
                { label: 'a1', value: 1 },
                { label: 'a2', value: 2 },
                { label: 'a3', value: 3 },
                { label: 'a4', value: 4 },
                { label: 'a5', value: 5 },
                { label: 'a6', value: 6 },
                { label: 'a7', value: 7 },
                { label: 'a8', value: 8 },
              ]}
            />
          )
        },
        checkbox: {
          initialValue: ['四川菜'],
          label: '选择你想吃的菜',
          children: (
            <Checkbox.Group>
              <div>菜系</div>
              <Checkbox value="四川菜">四川菜</Checkbox>
              <Checkbox value="湖北菜">湖北菜</Checkbox>
              <Checkbox value="西北菜">西北菜</Checkbox>
              <Checkbox value="新疆菜">新疆菜</Checkbox>
              <Checkbox value="东北菜">东北菜</Checkbox>
              <div style={{ marginTop: 10 }}>家常菜</div>
              <Checkbox value="红烧武昌鱼">红烧武昌鱼</Checkbox>
              <Checkbox value="麻婆豆腐">麻婆豆腐</Checkbox>
              <Checkbox value="北京烤鸭">北京烤鸭</Checkbox>
            </Checkbox.Group>
          )
        },
        checkboxOne: {
          inline: true,
          label: '一个多选条件',
          children: <Checkbox value="1">四川菜</Checkbox>
        },
        switch: {
          inline: true,
          initialValue: true,
          label: '开启',
          children: <Switch size="small" />
        },
        radioGroup: {
          inline: true,
          initialValue: '男',
          label: '单选',
          children: (
            <RadioGroup name="other">
              <Radio value="男">男</Radio>
              <Radio value="女">女</Radio>
              <Radio value="人妖" disabled>人妖</Radio>
              <Radio value="未知">未知</Radio>
            </RadioGroup>
          )
        },
        textarea: {
          initialValue: '',
          label: '多行文本输入框',
          children: <Textarea placeholder="请输入内容" />
        },
        terms: {
          validator: (currentValue) => {
            return !currentValue ? '必须统一服务条款' : null;
          },
          style: { marginBottom: 0 },
          children: <Checkbox value="1">已阅读并同意<a href="#">服务条款</a></Checkbox>
        }
      }}
    >
      {({ fields, state, canSubmit, resetForm }) => {
        console.log('fields:-->', state);
        return (
          <div style={{ maxWidth: 500 }}>
            <Row gutter={10}>
              <Col>{fields.userName}</Col>
              <Col>{fields.age}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.select}</Col>
              <Col>{fields.searchSelect}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.checkbox}</Col>
              <Col>{fields.checkboxOne}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.radioGroup}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.switch}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.textarea}</Col>
            </Row>
            <Row gutter={10}>
              <Col style={{ padding: '5px 0 10px 0' }}>
                {fields.terms}
              </Col>
            </Row>
            <Row gutter={10}>
              <Col fixed>
                <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                <Button type="light" onClick={resetForm}>重置表单</Button>
              </Col>
            </Row>
            <Row>
              <Col>
                <pre style={{ padding: '10px 0 0 10px' }}>
                  {JSON.stringify(state.current, null, 2)}
                </pre>
              </Col>
            </Row>
          </div>
        )
      }}
    </Form>
  )
}

水平登录栏

import React from 'react';
import { Form, Input, Row, Col, Notify, Button } from 'uiw';

export default function Demo() {
  return (
    <div>
      <Form
        onSubmit={({initial, current}) => {
          const errorObj = {};
          if (!current.username) {
            errorObj.username = '用户名不能为空!';
          }
          if (!current.password) {
            errorObj.password = '密码不能为空!';
          }
          if(Object.keys(errorObj).length > 0) {
            const err = new Error();
            err.filed = errorObj;
            Notify.error({ title: '提交失败!', description: '请确认提交表单是否正确!' });
            throw err;
          }
          console.log('-->>', initial, current);
          Notify.success({ title: '提交成功!', description: '恭喜你登录成功!' });
        }}
        onSubmitError={(error) => {
          if (error.filed) {
            return { ...error.filed };
          }
          return null;
        }}
        fields={{
          username: {
            labelClassName: 'fieldLabel',
            labelFor: 'username-inline',
            children: <Input preIcon="user" id="username-inline" />
          },
          password: {
            labelClassName: 'fieldLabel',
            labelFor: 'password-inline',
            children: <Input preIcon="lock" id="password-inline" type="password" />
          },
        }}
      >
        {({ fields, state, canSubmit, resetForm }) => {
          console.log('fields:', state);
          return (
            <div>
              <Row gutter={10}>
                <Col fixed>{fields.username}</Col>
                <Col fixed>{fields.password}</Col>
              </Row>
              <Row gutter={10}>
                <Col>
                  <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                  <Button type="danger" onClick={resetForm}>重置表单</Button>
                </Col>
              </Row>
            </div>
          )
        }}
      </Form>
    </div>
  );
}

登录

import React from 'react';
import { Form, Input, Row, Col, Checkbox, Notify, Button } from 'uiw';

export default function Demo() {
  return (
    <div>
      <Form
        onSubmit={({initial, current}) => {
          console.log('-->>', initial, current);
        }}
        fields={{
          username: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            labelFor: 'username',
            children: <Input preIcon="user" id="username" />
          },
          password: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            labelFor: 'password',
            children: <Input preIcon="lock" id="password" type="password" />
          },
          terms: {
            validator: (currentValue) => !currentValue ? '必须统一服务条款' : null,
            children: <Checkbox value="1">已阅读并同意</Checkbox>
          }
        }}
      >
        {({ fields, state, canSubmit }) => {
          console.log('fields:', state);
          return (
            <div>
              <Row>
                <Col fixed>{fields.username}</Col>
              </Row>
              <Row>
                <Col fixed>{fields.password}</Col>
              </Row>
              <Row>
                <Col fixed align="middle">{fields.terms}</Col>
                <Col fixed style={{ marginTop: -4 }}><a href="#">服务条款</a></Col>
              </Row>
              <Row>
                <Col fixed>
                  <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                </Col>
              </Row>
            </div>
          )
        }}
      </Form>
    </div>
  )
}

表单提交

import React from 'react';
import { Form, Input, Select, Row, Col, Button } from 'uiw';

export default function Demo() {
  return (
    <div>
      <Form
        onSubmit={({initial, current}) => {
          console.log('-->>', initial, current);
        }}
        fields={{
          firstName: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            inline: true,
            label: '姓氏',
            children: <Input />
          },
          lastName: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            initialValue: '先生',
            inline: true,
            label: '名字',
            children: <Input />
          },
          email: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            validator: (currentValue) => {
              return currentValue && currentValue.length < 2 ? 'Password must be 8+ characters' : null;
            },
            inline: true,
            label: 'Email',
            children: <Input />
          },
          select: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            inline: true,
            label: '选择器',
            children: (
              <Select>
                <Select.Option>Choose an item...</Select.Option>
                <Select.Option value="1">One</Select.Option>
                <Select.Option value="2">Two</Select.Option>
                <Select.Option value="3">Three</Select.Option>
                <Select.Option value="4">Four</Select.Option>
              </Select>
            ),
          },
        }}
      >
        {({ fields, state, canSubmit }) => {
          console.log('fields:', state);
          return (
            <div>
              <Row gutter={10} style={{ marginBottom: 10 }}>
                <Col>{fields.firstName}</Col>
                <Col>{fields.lastName}</Col>
              </Row>
              <Row gutter={10}>
                <Col>{fields.email}</Col>
                <Col>{fields.select}</Col>
              </Row>
              <Row gutter={10}>
                <Col />
                <Col fixed align="bottom"><Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button></Col>
              </Row>
            </div>
          )
        }}
      </Form>
    </div>
  )
}

自定义控件应用

下面实例是在 <Form /> 表单组件中,应用自定义 <CustomSelect /> 控件组件。

⚠️ 注意,自定义控件需要两个必要的 props 参数,valueonChange

  • value 用于值传递,
  • onChange(value) 用于值变更需要执行的回调函数,回调函数第一个参数必须是 value
import React from 'react';
import { Form, Row, Col, Dropdown, Menu, Icon, Button, Notify } from 'uiw';

// 自定义组件
function CustomSelect(props) {
  const { option = [], onChange } = props;
  const [value, setValue] = React.useState(props.value);
  const [isOpen, setIsOpen] = React.useState(false);

  React.useEffect(() => {
    if (value !== props.value) {
      setValue(props.value);
    }
  }, [props.value]);
  const label = option.find(item => value === item.value);
  return (
    <Dropdown
      trigger="click"
      onVisibleChange={(open) => setIsOpen(open)}
      isOpen={isOpen}
      menu={
        <Menu bordered style={{ minWidth: 120 }}>
          {option.map((item, idx) => (
            <Menu.Item active={value === item.value} key={idx} text={item.label}
              onClick={() => {
                setValue(item.value);
                setIsOpen(false)
                onChange && onChange(item.value);
              }}
            />
          ))}
        </Menu>
      }
    >
      <Button
        style={{
          boxShadow: 'inset 0 0 0 1px rgba(16, 22, 26, 0.2), inset 0 -1px 0 rgba(16, 22, 26, 0.1)'
        }}
        type="link"
      >
        {label.label}<Icon type={isOpen ? 'up' : 'down'} />
      </Button>
    </Dropdown>
  );
}

// 自定义组件应用实例
export default function Demo() {
  return (
    <div>
      <Form
        onSubmitError={(error) => {
          if (error.filed) {
            return { ...error.filed };
          }
          return null;
        }}
        onSubmit={({initial, current}) => {
          console.log('~~~', current);
          const errorObj = {};
          if (!current.select) {
            errorObj.select = '内容为空,请输入内容';
          }
          if(Object.keys(errorObj).length > 0) {
            const err = new Error();
            err.filed = errorObj;
            Notify.error({ title: '提交失败!', description: '请确认提交表单是否正确!' });
            throw err;
          }
          Notify.success({
            title: '提交成功!',
            description: `表单提交成功,内容为:${current.select},将自动填充初始化值!`,
          });
        }}
        fields={{
          select: {
            initialValue: 0,
            children: (
              <CustomSelect option={[
                { label: '请选择', value: 0 },
                { label: '经济舱', value: 1 },
                { label: '豪华经济舱', value: 2 },
                { label: '商务舱', value: 3 },
                { label: '头等舱', value: 4 },
              ]} />
            )
          },
        }}
      >
        {({ fields, state, canSubmit }) => {
          return (
            <div>
              <Row>
                <Col style={{ maxWidth: 300 }}>{fields.select}</Col>
              </Row>
              <Row>
                <Col fixed>
                  <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                </Col>
              </Row>
            </div>
          )
        }}
      </Form>
    </div>
  )
}

FormItem 竖排

对组件 FormItem 竖排展示示例。

⚠️ 注意:FormItem 组件只在 Form 组件中使用,在 @v4.10.4+ 以上版本可以当普通 form 使用。

import React from 'react';
import { Form, FormItem, Button, Input } from 'uiw';

export default function Demo() {
  const [formData, setFormData] = React.useState({});
  const handleSubmit = (_, e) => {
    e && e.preventDefault();
    const fData = new FormData(e.target);
    const data = {};
    fData.forEach((value, key) => { data[key] = value; });
    setFormData(data);
  }
  return (
    <Form onSubmit={handleSubmit} onReset={() => setFormData({})}>
      <FormItem
        label="可选字段"
        labelFor="item-basic-input"
        help={<span>在上面的字段中输入一个值</span>}
      >
        <Input id="item-basic-input" name="basic" type="text"/>
      </FormItem>
      <FormItem
        label="用户名"
        labelFor="item-username-input"
        help={(!formData.username || formData.username.length < 8) ? "用户名长度至少为8个字符串。" : "用户名正确 √ "}
        hasError={(!formData.username || formData.username.length < 8)}
      >
        <Input id="item-username-input" name="username" type="text"/>
      </FormItem>
      <FormItem>
        <Button type="success" htmlType="submit"> Submit </Button>
        <Button type="light" htmlType="reset"> Reset </Button>
      </FormItem>
      <pre>
      {JSON.stringify(formData, null, 2)}
      </pre>
    </Form>
  );
}

FormItem 横排

对组件 FormItem 横排展示示例。

⚠️ 注意:FormItem 组件只在 Form 组件中使用,在 @v4.10.4+ 以上版本可以当普通 form 使用。

import React from 'react';
import { Form, FormItem, Input } from 'uiw';

export default function Demo() {
  return (
    <Form>
      <FormItem
        inline={true}
        label="可选字段"
        labelFor="basic-input-inline"
        help={<span>在上面的字段中输入一个值</span>}
        onChange={() => {
          console.log('TEST::');
        }}
      >
        <Input id="basic-input-inline" name="basic" type="text"/>
      </FormItem>
      <FormItem
        inline={true}
        label="用户名"
        labelFor="username-input-inline"
        labelClassName="username"
        help="用户名长度至少为8个字符串。"
        hasError={true}
      >
        <Input id="username-input-inline" name="username" type="text"/>
      </FormItem>
    </Form>
  )
}

Form

| 参数 | 说明 | 类型 | 默认值 | |--------- |-------- |--------- |-------- | | fields | 设置字段 | object | - | | children | 回调 {fields, state, canSubmit, resetForm} | function | - | | onSubmit | 提交表单时调用 | (state: FormSubmitProps, event: React.FormEvent) => any | - | | afterSubmit @3.0.0+ | 提交回调 {initial, current} | function({ initial, current }) | - | | onChange | 表单发生改变回调函数 {initial, current} | function({ initial, current }) | - | | onSubmitError | 调用 onSubmit 抛出的任何错误。从字段名称返回对象映射。 | function | - | | resetOnSubmit | 在 onSubmit 成功后将表单重置为其初始状态。| bool | true | | ref | 返回form各种内部函数,可用于主动触发事件 | Ref | - |

fields

{
  firstName: {
    initialValue: '王',
    inline: true,
    label: '姓',
    labelClassName: 'fieldLabel',
    labelStyle: { width: 60 },
    // 验证,通过 `canSubmit()` 方法获得,提交按钮是否被禁用
    validator: (currentValue) => {},
    help: '帮助提示信息!',
    children: <Input type="number" />
  },
}

ref

const form = useRef()
render(<Form ref={form}/>)
form.current.onSubmit()                                   // 提交表单
form.current.resetForm()                                  // 重置form
const fieldValues = form.current.getFieldValues()         // 获取所有 field的 value对象
const error = form.current.getError()                     // 获取所有提交时验证错误
form.current.setFields({ /** [fieldName]: value **/  })   // 设置表单的值,覆盖 form所有 field的值
form.current.setFieldValue(fieldName, value)              // 对单个 field设置 value,如果 value为数组请自行深度拷贝后传值,以免破坏原数组

FormItem

| 参数 | 说明 | 类型 | 默认值 | |--------- |-------- |--------- |-------- | | label | 表单标题展示 | string | - | | required | 表单是否必填标识 | boolean | - | | labelClassName | 表单标题样式名称 | string | - | | labelStyle | 表单标题样式 | object | - | | labelFor | 列的宽度相对于同一网格中其他列的比率 | number | - | | help | 提示信息 | ReactNode | - | | hasError | 如果为true,则应用错误CSS。转动边框并帮助文字变红。 | number | - |