纯粹使用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的时候不销毁。

redux form的更多相关文章

  1. 2019 年 React 学习路线图(转)

    转自:https://www.infoq.cn/article/AEkiVAiJf25LZmoUe_yc 之前我们已经介绍了2019 年 Vue 学习路线图,而 React 作为当前应用最广泛的前端框 ...

  2. (转)2019年 React 新手学习指南 – 从 React 学习线路图说开去

    原文:https://www.html.cn/archives/10111 注:本文根据 React 开发者学习线路图(2018) 结构编写了很多新手如何学习 React 的建议.2019 年有标题党 ...

  3. 2019年React学习路线图

    作者|javinpaul 译者|无明 之前我们已经介绍了 2019 年 Vue 学习路线图,而 React 作为当前应用最广泛的前端框架,在 Facebook 的支持下,近年来实现了飞越式的发展,我们 ...

  4. webpack+react+redux+es6开发模式

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  5. 基于React,Redux以及wilddog的聊天室简单实现

    本文主要是使用ReactJs和Redux来实现一个聊天功能的页面,页面极其简单.使用React时间不长,还是个noob,有不对之处欢迎大家吐槽指正. 还要指出这里没有使用到websocket等技术来实 ...

  6. webpack+react+redux+es6

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  7. [Redux] Redux: Extracting Container Components -- AddTodo

    Code to be refactored: const AddTodo = ({ onAddClick }) => { let input; return ( <div> < ...

  8. 翻译:使用 Redux 和 ngrx 创建更佳的 Angular 2

    翻译:使用 Redux 和 ngrx 创建更佳的 Angular 2 原文地址:http://onehungrymind.com/build-better-angular-2-application- ...

  9. 详解 Node + Redux + MongoDB 实现 Todolist

    前言 为什么要使用 Redux? 组件化的开发思想解放了繁琐低效的 DOM 操作,以 React 来说,一切皆为状态,通过状态可以控制视图的变化,然后随着应用项目的规模的不断扩大和应用功能的不断丰富, ...

随机推荐

  1. jmeter常用的beanshell脚本

    时间戳下载文件保存响应内容断言连接数据库解析jsonlist递归创建多级目录 常用内置变量调用cmd文件GUI小命令 时间戳import java.text.SimpleDateFormat;impo ...

  2. gem install 提示rubygems.org连接不上的问题

    周五的时候安装compass时遇到的,总是报错,后来反应过来是被墙了.解决办法就是使用淘宝的rubygem的镜像 gem sources --remove https://rubygems.org/ ...

  3. vi/vim 中批量在行插入或删除指定字符

    1. 在每行的行首行尾插入指定字符      行首::%s/^/insert_word/ 行尾::%s/$/insert_word/  2. 在某些行的行进行替换       在2~50行首添加//号 ...

  4. hdu4027Can you answer these queries?(线段树)

    链接 算是裸线段树了,因为没个数最多开63次 ,开到不能再看就标记.查询时,如果某段区间被标记直接返回结果,否则继续向儿子节点更新. 注意用——int64 注意L会大于R 这点我很纠结..您出题人故意 ...

  5. js js弹出框、对话框、提示框、弹窗总结

    js弹出框.对话框.提示框.弹窗总结 一.JS的三种最常见的对话框 //====================== JS最常用三种弹出对话框 ======================== //弹 ...

  6. Java基础50题test1—不死神兔

    [不死神兔] 题目:古典问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少?  程序分析: 兔子的规律为数列 ...

  7. centos 7下Hadoop 2.7.2 伪分布式安装

    centos 7 下Hadoop 2.7.2 伪分布式安装,安装jdk,免密匙登录,配置mapreduce,配置YARN.详细步骤如下: 1.0 安装JDK 1.1 查看是否安装了openjdk [l ...

  8. 进程间通信,把字符串指针作为参数通过SendMessage传递给另一个进程,不起作用

    参数发送进程: CString csCmd=AfxGetApp()->m_lpCmdLine; if (!csCmd.IsEmpty()) { pWndPrev->SendMessage( ...

  9. SQLServer 错误: 15404,无法获取有关 Windows NT 组/ 用户 'WIN-8IVSNAQS8T7\Administrator' 的信息,错误代码 0x534。

    在自动清理日志的作业中,执行过程出现如下问题:“SQLServer 错误: 15404,无法获取有关 Windows NT 组/ 用户 'WIN-8IVSNAQS8T7\Administrator' ...

  10. java冒泡排序和快速排序代码

    冒泡排序: package nicetime.com; //基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,// 让较大的数往下沉,较小的往上 ...