redux form

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

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-formreact

redux-form

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

最简单的例子:api

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') );

基本的使用流程:promise

  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。其实也能够传递一个自定义的组件:dom

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的时候不销毁。

相关文章
相关标签/搜索