• redux form


    纯粹使用react进行表单校验:

    class MyForm extends React.Component{
        constructor(props){
            super(props)
            this.onAddrChange = this.onAddrChange.bind(this);
            this.state = {
                addr:""
            }
        }
        onAddrChange(evt){
            this.setState({
                addr:evt.target.value
            })
        }
        render(){
            return (
                <form>
                    <input type="text" value={this.state.addr} onChange={this.onAddrChange}/>
                </form>
            )
        }
    }

    可见为了维持双向绑定,以及校验信息。一个input至少需要3个以上的变量(value + change callback + verify message),表单比较大的话,代码逻辑十分复杂。

     

    redux-form-utils这个库没做好,内部报错了用不了。直接使用redux-form

    redux-form

    https://redux-form.com/6.6.3/examples/

    最简单的例子:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {createStore, combineReducers} from 'redux'
    import { Provider } from 'react-redux'
    import {reducer, Field, reduxForm} from "redux-form"
    
    const rootReducer = combineReducers({
        form: reducer
    });
    const store = createStore(rootReducer);
    class ContactForm extends React.Component{
        handleSubmit(e){
            console.log("submited")
            e.preventDefault()
        }
        render(){
            return (
                <form onSubmit={this.handleSubmit}>
                    <div>
                        <label htmlFor="email">Email</label>
                        <Field name="email" component="input" type="text" />
                    </div>
                    <button type="submit">Submit</button>
                </form>
            )
        }
    }
    
    ContactForm = reduxForm({
        form: 'contact'
    })(ContactForm)
    
    ReactDOM.render(
        <Provider store={store}>
            <ContactForm store={store}></ContactForm>
        </Provider>,
        document.getElementById('root')
    );

    基本的使用流程:

    1. 创建store,使用的redux-form中的redux,这样这个store就具备了处理redux-form内置action的能力了。
    2. 定义表单组件,在表单中使用Field作为input(自动把value、onChange等封装到了input中,用于维持双向绑定)。
    3. 使用reduxForm增强我们的表单组件,增强后的表单可以从context中获取store,并从store中获取state,state中的数据通过props传递给我们的表单组件(除了可以从props中获取store的state外,还可以获取处理表单提交的函数),即使表单具备了与store通信的能力。
    4. 在增强后的表单组件外部通过provider提供一个store,给里面的子组件调用。

    理解以上的更新关系很重要:

      

     

    组件树中的组件的渲染顺序都是深度优先,即 所有的Field会被首先执行,内部执行的过程中就把name值设置到store上了。

    Field

    这个内置的组件用于增强我们的input。以上的component属性中使用的是input。其实也可以传递一个自定义的组件:

    function MyCs(props){
        console.log(props)
        return <div></div>
    }
    <Field name="email" component={MyCs} type="text" x="123"/>

    输出的数据:

    以上的Field标签渲染出来的结果就是一个 <div></div>。可见,通过Field组件,我们可以封装一些表单中的子组件。

    input和meta中的属性参考:https://redux-form.com/7.2.0/docs/api/field.md/#props

    表单校验

    查看以上的关系图可以发现,onChange是定义在增强后的组件中的,在那里可以最早获取到表单数据,也可以把校验相关的函数封装进去。因为这个增强后的组件是通过reduxForm生成的,自然而然校验相关的函数也应该通过reduxForm传递进去(通过一个配置对象传入):

    export default reduxForm({
      form: 'syncValidation', 
      validate, // <--- validation function given to redux-form
      warn // <--- warning function given to redux-form
    })(SyncValidationForm)

     

    以上函数中可以通过参数获取到最新的表单数据,返回一个校验信息对象,这个对象中映射了指定name的input与之校验信息的对应关系。

    使用的方式如下:

    校验函数(error|warning)返回  { age: "age is required" }
    对于组件:<Field name="age" type="number" component={renderField} label="Age" /> 。在renderField这个组件内,可以通过参数props.meta.error|warning获取到 "age is required" 这个校验信息,然后组件内把这个错误信息显示出来即可。

    error和warning函数的返回对象都可以用在Field所指定的组件内部,但他们的区别是什么?只要error有值,则表单onSubmit就不会触发,而warning不影响提交。

    Field级别的校验

    以上的校验是针对整个表单的校验,所有校验逻辑集中在一个函数中。使用Field级别的校验可以把每种校验类型拆分出来,针对Field可实现更加灵活的校验控制。

    const required = value => (value ? undefined : 'Required')
    const alphaNumeric = value =>
        value && /[^a-zA-Z0-9 ]/i.test(value)
            ? 'Only alphanumeric characters'
            : undefined
    
    <Field
        name="username"
        type="text"
        component={renderField}
        label="Username"
        validate={[required, maxLength15, minLength2]}
        warn={alphaNumeric}
    />

     返回undefined则代表校验通过,返回的字符串和上面一样,可以在对应的Field指定的组件内通过props.meta.warning | error 获取。

    异步校验

    reduxForm增强表单的时候配置对象如下:

    export default reduxForm({
      form: 'asyncValidation', 
      validate,
      asyncValidate,
      asyncBlurFields: ['username']
    })(AsyncValidationForm)

    asyncBlurFields:指定当哪些FieldonBlur的时候触发异步校验

    asyncValidate:指定异步校验的函数,在这个函数中返回一个promise,promise中如果抛出一个校验信息对象,则代表校验失败

    服务器端校验

    以上输出前端的校验,怎么处理服务端的校验信息呢?

    在submit的回调函数中,返回一个promise,在这个promise中进行异步请求。这个promise被resolve的话则提交完成。如果内部抛出SubmissionError错误,则代表校验失败

    throw new SubmissionError({
        password: 'Wrong password',
        _error: 'Login failed!'
    })

     被包裹的对象和之前校验函数返回的对象用法一致。

    表单状态的初始化

    把reduxForm增强后的表单,进行connect;而表单中Field字段,默认读取props.initialValues.xxname的值,所以简单的同步初始化方式是:

    const data = {
        firstName: 'Jane',
        lastName: 'Doe',
    }
    InitializeFromStateForm = connect(
        state => ({
            initialValues: data
        })
    )(InitializeFromStateForm)

    因为connect的作用是实现自动关联。所以只要在异步请求后,调用dispatch,把异步数据发送给reducer,上面就不用直接读取data,而是从state中读取异步数据,界面能自动刷新,这样就实现了异步的状态初始化,简单的例子可以查看 这里

    注意:因为props会层层传递,所以,以上connect中mapStateToProps(state)和mapDispatchProps(dispatch)返回的值也可以在表单中通过props.xxx来使用,针对这一点,允许我们在表单中使用一些表单值以外的变量

    获取处理表单数据

    假如我有一个需求,当点击了checkBox,则把一个表单模块显示出来。因为表单项的值都交由框架进行处理,所以没办法在jsx中直接访问。但通过connect映射出来的props可以在jsx中直接使用,我们只需要在映射的时候获取到表单项的值,然后对这些值进行映射,把新的值返回。这样就可以在jsx中访问了。通过redux-form提供的selector可以获取到表单项的值。

    FieldArray

    用于相同输入组件的动态添加和删除。

    表单值的格式化

    对用户输入的值进行格式化,格式化后的值可以在onSubmit中获取,以及会显示到界面上。使用方式和Field级别的校验类似,在Field上添加一个字段,指定一个格式化函数即可。

    Immutable 

    将程序的状态全部设置为不可变,可以把从redux-form获取的值全部改成从redux-form/immutable中获取,校验以及提交的时候,获取值从values.xxx 更改为 values.Get("xxx") 即可。

    向导型的表单

    把表单拆分成多个部分,每个部分放在不同的页面中显示,每个页面填写完,再跳转到下一个页面继续填写,类似于向导页。

    以上说切换页面,实际是在切换组件的显示,每个组件代表一个表单页,内部有一个完成的表单,但在redux-from增强的时候必须指定这多个表单的具有相同的form值,而且unmount的时候不销毁。

     

  • 相关阅读:
    Servlet第一篇【介绍Servlet、HTTP协议、WEB目录结构、编写入门Servlet程序、Servlet生命周期】
    IDEA配置Tomcat
    Tomcat【介绍Tomcat、结构目录、虚拟目录、临时域名、虚拟主机、体系结构】
    Mysql免安装版配置【图文版和文字版】
    JDBC第四篇--【数据库连接池、DbUtils框架、分页】
    JDBC第三篇--【事务、元数据、改造JDBC工具类】
    JDBC第二篇--【PreparedStatment、批处理、处理二进制、自动主键、调用存储过程、函数】
    JDBC第一篇--【介绍JDBC、使用JDBC连接数据库、简单的工具类】
    DTD
    第五周项目2-对象作为数据成员
  • 原文地址:https://www.cnblogs.com/hellohello/p/8043523.html
Copyright © 2020-2023  润新知