• react之form表单工具:formik+yup


    从网上搜到的form表单解决方案如下:

    1.uform,地址:https://uformjs.org/#/MpI2Ij/dNFzFyTb

    UForm 和核心特性:

    • 基于标准 JSON Schema 协议,数据化构建表单
    • 基于 rxjs 对表单内部的副作用做统一管理,轻松解决各种复杂联动校验场景
    • 支持各种表单布局方案
    • 支持可视化构建表单
    • 支持自定义组件扩展
    • 分布式状态管理,表单性能更高

    不足:基于styled-components来开发的,涉及到的自定义样式主要是Form和FormItem层面上的样式。

              样式很难hack,很难自定义。

    2.react-jsonschema-form,地址:https://github.com/rjsf-team/react-jsonschema-form

    • 强耦合 Bootstrap,不方便扩展
    • JSON 描述不能很好的在 JSX 中描述
    • 没能很好的解决表单布局,表单联动的各种复杂问题
    • 性能不行,内部数据管理走的 React 的全量 rerender 机制来做数据同步

    最终使用:Formik+yup组合来处理表单。github上star数目最多。

           formik地址:https://github.com/jaredpalmer/formik

           yup地址:https://github.com/jquense/yup

    使用实例:

       a. 不与yup结合,只使用formik,form表单代码如下:

      

    import React from 'react';
    import { useFormik } from 'formik';
    
    const validate = values => {
      const errors = {};
      if (!values.firstName) {
        errors.firstName = 'Required';
      } else if (values.firstName.length > 15) {
        errors.firstName = 'Must be 15 characters or less';
      }
    
      if (!values.lastName) {
        errors.lastName = 'Required';
      } else if (values.lastName.length > 20) {
        errors.lastName = 'Must be 20 characters or less';
      }
    
      if (!values.email) {
        errors.email = 'Required';
      } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$/i.test(values.email)) {
        errors.email = 'Invalid email address';
      }
    
      return errors;
    };
    
    const SignupForm = () => {
      const formik = useFormik({
        initialValues: {
          firstName: '',
          lastName: '',
          email: '',
        },
        validate,
        onSubmit: values => {
          alert(JSON.stringify(values, null, 2));
        },
      });
      return (
        <form onSubmit={formik.handleSubmit}>
          <label htmlFor="firstName">First Name</label>
          <input
            id="firstName"
            name="firstName"
            type="text"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.firstName}
          />
          {formik.touched.firstName && formik.errors.firstName ? (
            <div>{formik.errors.firstName}</div>
          ) : null}
          <label htmlFor="lastName">Last Name</label>
          <input
            id="lastName"
            name="lastName"
            type="text"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.lastName}
          />
          {formik.touched.lastName && formik.errors.lastName ? (
            <div>{formik.errors.lastName}</div>
          ) : null}
          <label htmlFor="email">Email Address</label>
          <input
            id="email"
            name="email"
            type="email"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.email}
          />
          {formik.touched.email && formik.errors.email ? (
            <div>{formik.errors.email}</div>
          ) : null}
          <button type="submit">Submit</button>
        </form>
      );
    };

    以上代码也是非常冗余。form表单校验全靠手动维护,很不方便。接下来

    b.与yup结合。formik暴露了配置validationSchema与yup结合。示例如下

    import React from 'react';
    import { useFormik } from 'formik';
    import * as Yup from 'yup';
    
    const SignupForm = () => {
      const formik = useFormik({
        initialValues: {
          firstName: '',
          lastName: '',
          email: '',
        },
        validationSchema: Yup.object({
          firstName: Yup.string()
            .max(15, 'Must be 15 characters or less')
            .required('Required'),
          lastName: Yup.string()
            .max(20, 'Must be 20 characters or less')
            .required('Required'),
          email: Yup.string()
            .email('Invalid email address')
            .required('Required'),
        }),
        onSubmit: values => {
          alert(JSON.stringify(values, null, 2));
        },
      });
      return (
        <form onSubmit={formik.handleSubmit}>
          <label htmlFor="firstName">First Name</label>
          <input
            id="firstName"
            name="firstName"
            type="text"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.firstName}
          />
          {formik.touched.firstName && formik.errors.firstName ? (
            <div>{formik.errors.firstName}</div>
          ) : null}
          <label htmlFor="lastName">Last Name</label>
          <input
            id="lastName"
            name="lastName"
            type="text"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.lastName}
          />
          {formik.touched.lastName && formik.errors.lastName ? (
            <div>{formik.errors.lastName}</div>
          ) : null}
          <label htmlFor="email">Email Address</label>
          <input
            id="email"
            name="email"
            type="email"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.email}
          />
          {formik.touched.email && formik.errors.email ? (
            <div>{formik.errors.email}</div>
          ) : null}
          <button type="submit">Submit</button>
        </form>
      );
    };

      如上,dom节点处的代码依然是比较复杂和重复的。

    c.formik提供了一个Field组件

    import React from 'react';
    import { Formik, Field, Form, ErrorMessage } from 'formik';
    import * as Yup from 'yup';
    
    const SignupForm = () => {
      return (
        <Formik
          initialValues={{ firstName: '', lastName: '', email: '' }}
          validationSchema={Yup.object({
            firstName: Yup.string()
              .max(15, 'Must be 15 characters or less')
              .required('Required'),
            lastName: Yup.string()
              .max(20, 'Must be 20 characters or less')
              .required('Required'),
            email: Yup.string()
              .email('Invalid email address')
              .required('Required'),
          })}
          onSubmit={(values, { setSubmitting }) => {
            setTimeout(() => {
              alert(JSON.stringify(values, null, 2));
              setSubmitting(false);
            }, 400);
          }}
        >
          <Form>
            <label htmlFor="firstName">First Name</label>
            <Field name="firstName" type="text" />
            <ErrorMessage name="firstName" />
            <label htmlFor="lastName">Last Name</label>
            <Field name="lastName" type="text" />
            <ErrorMessage name="lastName" />
            <label htmlFor="email">Email Address</label>
            <Field name="email" type="email" />
            <ErrorMessage name="email" />
            <button type="submit">Submit</button>
          </Form>
        </Formik>
      );
    };

    Field组件默认渲染为input。

    <Field name="message" as="textarea"  className="form-input"/>
    
    // <select className="my-select"/>
    <Field name="colors" as="select" className="my-select">
      <option value="red">Red</option>
      <option value="green">Green</option>
      <option value="blue">Blue</option>
    </Field>

    最后,再进一步用法:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Formik, Form, useField } from 'formik';
    import styled from '@emotion/styled';
    import * as Yup from 'yup';
    
    const MyTextInput = ({ label, ...props }) => {
      // useField() returns [formik.getFieldProps(), formik.getFieldMeta()]
      // which we can spread on <input> and also replace ErrorMessage entirely.
      const [field, meta] = useField(props);
      return (
        <>
          <label htmlFor={props.id || props.name}>{label}</label>
          <input className="text-input" {...field} {...props} />
          {meta.touched && meta.error ? (
            <div className="error">{meta.error}</div>
          ) : null}
        </>
      );
    };
    
    const MyCheckbox = ({ children, ...props }) => {
      // We need to tell useField what type of input this is
      // since React treats radios and checkboxes differently
      // than inputs/select/textarea.
      const [field, meta] = useField({ ...props, type: 'checkbox' });
      return (
        <>
          <label className="checkbox">
            <input type="checkbox" {...field} {...props} />
            {children}
          </label>
          {meta.touched && meta.error ? (
            <div className="error">{meta.error}</div>
          ) : null}
        </>
      );
    };
    
    // Styled components ....
    const StyledSelect = styled.select`
      /** ... * /
    `;
    
    const StyledErrorMessage = styled.div`
      /** ... * /
    `;
    
    const StyledLabel = styled.label`
     /** ...* /
    `;
    
    const MySelect = ({ label, ...props }) => {
      const [field, meta] = useField(props);
      return (
        <>
          <StyledLabel htmlFor={props.id || props.name}>{label}</StyledLabel>
          <StyledSelect {...field} {...props} />
          {meta.touched && meta.error ? (
            <StyledErrorMessage>{meta.error}</StyledErrorMessage>
          ) : null}
        </>
      );
    };
    
    // And now we can use these
    const SignupForm = () => {
      return (
        <>
          <h1>Subscribe!</h1>
          <Formik
            initialValues={{
              firstName: '',
              lastName: '',
              email: '',
              acceptedTerms: false, // added for our checkbox
              jobType: '', // added for our select
            }}
            validationSchema={Yup.object({
              firstName: Yup.string()
                .max(15, 'Must be 15 characters or less')
                .required('Required'),
              lastName: Yup.string()
                .max(20, 'Must be 20 characters or less')
                .required('Required'),
              email: Yup.string()
                .email('Invalid email address')
                .required('Required'),
              acceptedTerms: Yup.boolean()
                .required('Required')
                .oneOf([true], 'You must accept the terms and conditions.'),
              jobType: Yup.string()
                .oneOf(
                  ['designer', 'development', 'product', 'other'],
                  'Invalid Job Type'
                )
                .required('Required'),
            })}
            onSubmit={(values, { setSubmitting }) => {
              setTimeout(() => {
                alert(JSON.stringify(values, null, 2));
                setSubmitting(false);
              }, 400);
            }}
          >
            <Form>
              <MyTextInput
                label="First Name"
                name="firstName"
                type="text"
                placeholder="Jane"
              />
              <MyTextInput
                label="Last Name"
                name="lastName"
                type="text"
                placeholder="Doe"
              />
              <MyTextInput
                label="Email Address"
                name="email"
                type="email"
                placeholder="jane@formik.com"
              />
              <MySelect label="Job Type" name="jobType">
                <option value="">Select a job type</option>
                <option value="designer">Designer</option>
                <option value="development">Developer</option>
                <option value="product">Product Manager</option>
                <option value="other">Other</option>
              </MySelect>
              <MyCheckbox name="acceptedTerms">
                I accept the terms and conditions
              </MyCheckbox>
    
              <button type="submit">Submit</button>
            </Form>
          </Formik>
        </>
      );
    };

    实际使用:

     1.新建一个form表单元素的通用js代码如下:

    import { useField } from 'formik';
    import React from 'react';
    export const MyTextInput = ({ label, ...props }) => {
        // useField() returns [formik.getFieldProps(), formik.getFieldMeta()]
        // which we can spread on <input> and also replace ErrorMessage entirely.
        const [field, meta] = useField(props);
        return (
            <>
            <label htmlFor={props.id || props.name}>{label}</label>
            <input className={meta.touched && meta.error?'text-input error-input':'text-input'}  {...field} {...props} />
            {meta.touched && meta.error ? (
                <div className="error">{meta.error=="请输入"?meta.error+label:label+meta.error}</div>
            ) : null}
            </>
        );
    };
    export const MyCheckbox = ({ children, ...props }) => {
        // We need to tell useField what type of input this is
        // since React treats radios and checkboxes differently
        // than inputs/select/textarea.
        const [field, meta] = useField({ ...props, type: 'checkbox' });
        return (
            <>
            <label className="checkbox">
                <input type="checkbox"  {...field} {...props} />
                {children}
            </label>
            {meta.touched && meta.error ? (
                <div className="error">{meta.error}</div>
            ) : null}
            </>
        );
    };
    export const MySelect = ({ label, ...props }) => {
        const [field, meta] = useField(props);
        return (
            <>
            <label htmlFor={props.id || props.name}>{label}</label>
            <select {...field} {...props} />
            {meta.touched && meta.error ? (
                <div className="error">{meta.error}</div>
            ) : null}
            </>
        );
    };
    View Code

    2.新建一个统一校验规则js,代码如下:

    import * as Yup from 'yup'
    /* 设置提示文字 */
    export function yupSetLocale(){
        Yup.setLocale({
            mixed: {
                required:'请输入'
            },
            string: {
                max: '字符最多${max}个'
            },
        });
    }
    export const password = {'message':'规则提示信息','reg':/^(?![a-zA-z]+$)(?!d+$)(?![!@#$%^&*,.?></]+$)(?![a-zd]+$)(?![A-Zd]+$)(?![a-z!@#$%^&*,.?></]+$)(?![d!@#$%^&*,.?></]+$)(?![A-Z!@#$%^&*,.?></]+$)[a-zA-zd!@#$%^&*,.?></]{8,20}$/}
    View Code

    3.使用

    import React ,{Component} from 'react'
    import { Formik, Field, Form, ErrorMessage } from 'formik';
    import {MyTextInput,MyCheckbox,MySelect} from './../../../common/formItem'
    import * as Yup from 'yup';
    import * as myYup from './../../../common/validator';
    /* 设置提示文字 */
    myYup.yupSetLocale();
     const SignupForm = () => {
                const formik = Yup.object({
                    firstName: Yup.string()
                        .required().matches(myYup.password.reg,myYup.password.message),
                    lastName: Yup.string()
                        .max(20)
                        .required(),
                    email: Yup.string().email('Invalid email address').required(),
                    acceptedTerms: Yup.boolean()
                        .required('请选择')
                        .oneOf([true], '你必须勾选'),
                    jobType: Yup.string().required('请选择'),
                })
                return (
                    <Formik
                        initialValues={{ firstName: '', lastName: '', email: '' }}
                        validationSchema={formik}
                        onSubmit={(values, { setSubmitting }) => {
                            setTimeout(() => {
                                alert(JSON.stringify(values, null, 2));
                                setSubmitting(false);
                            }, 400);
                        }}
                    >
                        <Form>
                            <div>
                            <MyTextInput
                                label="姓名"
                                name="firstName"
                                type="text"
                                placeholder="Jane"
                            />
                            </div>
                            <div>
                            <MyTextInput
                                label="用户名"
                                name="lastName"
                                type="text"
                                placeholder="Doe"
                            />
                            </div>
                            <div>
                            <MyTextInput
                                label="邮箱"
                                name="email"
                                type="email"
                                placeholder="jane@formik.com"
                            />
                            </div>
                            <div>
                            <MySelect label="Job Type" name="jobType">
                                <option value="">Select a job type</option>
                                <option value="designer">Designer</option>
                                <option value="development">Developer</option>
                                <option value="product">Product Manager</option>
                                <option value="other">Other</option>
                            </MySelect>
                            </div>
                            <div>
                            <MyCheckbox name="acceptedTerms">
                                I accept the terms and conditions
                            </MyCheckbox>
                            </div>
                            <div>
                            <button type="submit" >Submit</button>
                            </div>
                        </Form>
                    </Formik>
                );
            };
    View Code
  • 相关阅读:
    reduce常规教程
    新的职业计划
    vscode插件 console helper
    webpack的loader和plugin的区别
    for in 和for of的区别
    https://www.codegrepper.com/index.php
    防抖和节流
    千万级别的表分页查询非常慢,怎么办?
    https://gitee.com/knif/AcceleratorKunn?_from=gitee_search
    17个可以实现微前端的方案
  • 原文地址:https://www.cnblogs.com/yeduweichengzhaoyu/p/12090440.html
Copyright © 2020-2023  润新知