Redux-Form 基础使用

有关于 redux-form 的 数据流、表单value的生命周期 信息请参考:

官方文档:(下面示例代码在官网可查阅)

http://redux-form.com/6.8.0/docs/GettingStarted.md/

中文翻译文档:

https://segmentfault.com/a/1190000010088546#articleHeader1

本文介绍表单中常用的几处功能:

[1.Field组件使用]

所有需要与 store 数据连接的表单组件,都可以用 <Field/>。在正确使用它之前,有三条基本概念需要了解清楚:

1. 必须包含 name 属性。可以是简单的字符串,如 userName、password,也可以是复杂的结构,如 contact.billing.address[2].phones[1].areaCode。

2. 必须包含 component 属性。可以是一个组件、无状态组件或者DOM所支持的默认的标签(input、textarea、select)。

3. 其他所有属性会通过prop传递到元素生成器中。如 className

# Field name属性:

a. 表示在Submit 表单时所带参数values对象中的属性名

如:

<Field name="username" type="text" component=‘input‘ label="Username"/>
<Field name="phone" type="text" component=‘input‘ label="Phone"/>
<Field name="age" type="text" component=‘input‘ label="Age"/>

values:{
username:‘小明‘,
phone:13510535555,
age:20
}

b. 在初始化表单数据后的 initialValues 对象,该对象中的属性对应 Field name 的名称,值自动映射

如:具体见第2部分

initialValues : {
username:‘小明‘,
phone:13510535555,
age:20
}

c. 供选取表单所填值

具体见第3部分

# Field component 属性:组件class 定义

// MyCustomInput.js
import React, { Component } from ‘react‘

class MyCustomInput extends Component {
  render() {
    const { input: { value, onChange } } = this.props
    return (
      <div>
        <span>The current value is {value}.</span>
        <button type="button" onClick={() => onChange(value + 1)}>Inc</button>
        <button type="button" onClick={() => onChange(value - 1)}>Dec</button>
      </div>
    )
  }
}

// 调用
import MyCustomInput from ‘./MyCustomInput‘
<Field name="myField" component={MyCustomInput}/>

# Field component 属性:无状态组件

// outside your render() method
const renderField = (field) => (
    <div className="input-row">
      <input {...field.input} type="text"/>
      {field.meta.touched && field.meta.error &&
       <span className="error">{field.meta.error}</span>}
    </div>
  )

// inside your render() method
<Field name="myField" component={renderField}/>

注:必须在你的 render() 方法外定义它,否则它每次渲染都会被重建,并且由于组件的 prop 会变,就会强制 <Field/> 进行渲染。如果你在 render() 内部定义无状态组件,不但会拖慢你的app,而且组件的input每次都会在组件重新渲染的时候失去焦点。

# Field component 属性:string: input, select, or textarea

比如创建一个文字输入框组件:

<Field component="input" type="text"/>

[2.初始化表单值设置]

a. 通过 initialValues 属性或 reduxForm() 配置的参数所提供的数据,被加载到表单 state 中,并且把这些初始化数据作为原始数据(pristine)。当 reset() 触发的时候,也会返回这些值 pristine。

如:示例 http://redux-form.com/6.8.0/examples/initializeFromState/

(图1)点击 Load Account 初始化数据并作为原始数据

(图2)修改 Age 为 45,点击Undo Changes 触发 reset() ,返回 pristine 值,如下(图3)

b. 除了保存这些 pristine 值,初始化您表单的这个操作也会替换表单里已经存在的值。

表单已存在下面值:

点击 Load Account 初始化数据后:原添加的表单数据被初始化数据替换

c. 在许多应用中,这些值可能是来自服务器并且储存在其他 reducer 中的。想要得到这些值,你需要使用 connect() 去自己链接 state 然后映射这些数据到您的 initialValues 属性里。

如示例代码:

// 用reduxForm()装饰。它将读取connect()提供的initialValues支持
InitializeFromStateForm = reduxForm({
    form: ‘initializeFromState‘,
})(InitializeFromStateForm);

InitializeFromStateForm = connect(
    state => ({             //mapStateToProps
        initialValues: state.account.data,
    }),
    { load: loadAccount }, //mapDispatchToProps,将load动作注入到reduxForm,派发后初始化数据
)(InitializeFromStateForm);

[3.选取表单所填值]

a. 通过formValueSelector 选取表单值

先通过 store 直接 connect() 表单的值,再通过 redux-form 提供的选择器formValueSelector 选取表单值。

如示例代码:

import { Field, reduxForm, formValueSelector } from ‘redux-form‘;
// Decorate with reduxForm(). It will read the initialValues prop provided by connect()
SelectingFormValuesForm = reduxForm({
  form: ‘selectingFormValues‘,// a unique identifier for this form
})(SelectingFormValuesForm)

// Decorate with connect to read form values
const selector = formValueSelector(‘selectingFormValues‘) // <-- same as form name
SelectingFormValuesForm = connect(state => {
  // can select values individually
  const hasEmailValue = selector(state, ‘hasEmail‘)
  const favoriteColorValue = selector(state, ‘favoriteColor‘)
  // or together as a group
  const { firstName, lastName } = selector(state, ‘firstName‘, ‘lastName‘)
  return {
    hasEmailValue,
    favoriteColorValue,
    fullName: `${firstName || ‘‘} ${lastName || ‘‘}`
  }
})(SelectingFormValuesForm)

export default SelectingFormValuesForm

注:hasEmail、favoriteColor、firstName、lastName 为表单 Field 属性 name 值

警告: 需要节制使用这个机制,因为这样的话,表单里的某一个值一旦发生改变,就会重新渲染您的组件。

b. 通过 Selectors 中的 getFormValues 选取表单值

redux-form 提供了一系列有用的 Redux state 拾取器,可以在app的任何地方任何表单内拾取 state 上的数据。

下列所有拾取器拥有统一的使用方法: 他们都(除了getFormNames)使用表单的名字,来创建一个拾取器,无论表单的 state是什么。

import {
  getFormValues,
  getFormInitialValues,
  getFormSyncErrors,
  getFormMeta,
  getFormAsyncErrors,
  getFormSyncWarnings,
  getFormSubmitErrors,
  getFormNames,
  isDirty,
  isPristine,
  isValid,
  isInvalid,
  isSubmitting,
  hasSubmitSucceeded,
  hasSubmitFailed
} from ‘redux-form‘

MyComponent = connect(
  state => ({
    values: getFormValues(‘myForm‘)(state),
    initialValues: getFormInitialValues(‘myForm‘)(state),
    syncErrors: getFormSyncErrors(‘myForm‘)(state),
    fields: getFormMeta(‘myForm‘)(state),
    asyncErrors: getFormAsyncErrors(‘myForm‘)(state),
    syncWarnings: getFormSyncWarnings(‘myForm‘)(state),
    submitErrors: getFormSubmitErrors(‘myForm‘)(state),
    names: getFormNames(‘myForm‘)(state),
    dirty: isDirty(‘myForm‘)(state),
    pristine: isPristine(‘myForm‘)(state),
    valid: isValid(‘myForm‘)(state),
    invalid: isInvalid(‘myForm‘)(state),
    submitting: isSubmitting(‘myForm‘)(state),
    submitSucceeded: hasSubmitSucceeded(‘myForm‘)(state),
    submitFailed: hasSubmitFailed(‘myForm‘)(state)
  })
)(MyComponent)

[4.格式化值 Field Normalizing ]

当您需要在用户输入和 store 中的数据之间施加某些控制,你可以使用 normalizer。normalizer 就是一个每当值改变是,可以在保存到 store 之前进行某些转换的一个函数。

一个常用的例子:你需要一个某些经过格式化的值,比如电话号码或信用卡号。

Normalizers 传递了4个参数:

● value - 你设置了 normalizer 字段的值

● previousValue - 这个值最近一次变化之前的一个值

● allValues - 表单中,所有字段当前的值

● previousAllValues - 表单中,所有字段在最近一次变化前的值

这些可以使你基于表单中另外一个字段而限制某个特定的字段。比如例子中的字段最小最大值:这里你不能设置 min 中的值比 max中的值大,不能设置 max 中的值比 min 的值更小(下面有代码)

const upper = value => value && value.toUpperCase()
const lower = value => value && value.toLowerCase()
const lessThan = otherField => (value, previousValue, allValues) =>
  parseFloat(value) < parseFloat(allValues[otherField]) ? value : previousValue
const greaterThan = otherField => (value, previousValue, allValues) =>
  parseFloat(value) > parseFloat(allValues[otherField]) ? value : previousValue

//下面是对电话号码处理的逻辑
const normalizePhone = value => {
  if (!value) {
    return value
  }

  const onlyNums = value.replace(/[^\d]/g, ‘‘)
  if (onlyNums.length <= 3) {
    return onlyNums
  }
  if (onlyNums.length <= 7) {
    return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3)}`
  }
  return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3, 6)}-${onlyNums.slice(6, 10)}`
}

[5.数据验证,支持同步验证和异步验证]

a. 同步验证

同步的表单验证,包括了错误和警告型配置。如示例代码:

e = values => {
    const errors = {}
    if (!values.username) {
        errors.username = ‘Required‘
    } else if (values.username.length > 15) {
        errors.username = ‘Must be 15 characters or less‘
    }
    if (!values.email) {
        errors.email = ‘Required‘
    } else if (!/^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
        errors.email = ‘Invalid email address‘
    }
    if (!values.age) {
        errors.age = ‘Required‘
    } else if (isNaN(Number(values.age))) {
        errors.age = ‘Must be a number‘
    } else if (Number(values.age) < 18) {
        errors.age = ‘Sorry, you must be at least 18 years old‘
    }
    return errors
}

const warn = values => {
    const warnings = {}
    if (values.age < 19) {
        warnings.age = ‘Hmm, you seem a bit young...‘
    }
    return warnings
}

/**
 * renderField 接收的是一个对象(Field组件props):
 * { name, value, input, meta: {touched, error, warning} }
 */
const renderField = ({ input, label, type, meta: {touched, error, warning, valid } }) => (
    <div>
        <label>{label}</label>
        <div>
            <input {...input} placeholder={label} type={type}/>
            {touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
        </div>
    </div>
)

/**
 * 同步验证
 * @param props
 * @returns {XML}
 * @constructor
 */
const SyncValidationForm = (props) => {
    const { handleSubmit, pristine, reset, submitting } = props
    return (
        <form onSubmit={handleSubmit}>
            <Field name="username" type="text" component={renderField} label="Username"/>
            <Field name="email" type="email" component={renderField} label="Email"/>
            <Field name="age" type="number" component={renderField} label="Age"/>
            <div>
                <button type="submit" disabled={submitting}>Submit</button>
                <button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
            </div>
        </form>
    )
}

export default reduxForm({
    form: ‘syncValidation‘,
    validate,               //redux-form的验证功能
    warn,                    //redux-form的警告功能
})(SyncValidationForm)

注:

1. validate、warn 验证函数命名以及返回对象命名:errors、warnings是固定的,不可更改,最终返回的错误信息格式:{ field1: <String>, field2: <String> }

2. input、meta: {touched, error, warning } 参数属性命名固定写法,不可更改

如错误信息:{username:‘用户名不能为空‘,email:‘右键不能为空‘}, username的error值对应 Field中属性meta.error, username的warning值对应 Field中属性meta.warning;

3. Field 属性 meta.valid 与 props.valid的区别:

前者验证 当前Field字段值是否通过。后者判断整个表单内的所有Field是否通过验证,其中有一个为false,则props.valid为false

4. meta.touched 是否触动结束(获得焦点时为false,表示正在输入;反之,失去焦点时为true)

b. 异步验证

服务器表单验证的方式比较推荐使用Submit Validation,但是可能存在当您填写表单的时候,同时需要服务器端来验证。有一个经典的例子是当一个用户选取一个值,比如用户名,它必须是您系统中唯一的一个值。

为了写一个异步的表单验证,需要给 redux-form 提供一个异步验证的函数(asyncValidation)用来提供一个可以从表单获取数据的一个对象,然后 Redux 分发这个函数,返回一个状态为拥有一个错误对象的 rejects或状态为 reslove 的 promise 对象。

您需要同时指定某几个字段,通过 asyncBlurFields 的属性配置,来标记是否需要在他们失去焦点的时候触发这个异步验证。

重点:

1.异步验证会在 onSubmit 之前被调用。

2.当一个字段的同步验证错误时,那它的失去焦点的时候将不会触发异步验证。

如示例代码:

export default reduxForm({
    form: ‘asyncValidation‘, // a unique identifier for this form
    validate,                     //同步验证函数
    asyncValidate,                              //异步验证函数
    asyncBlurFields: [‘username‘],     //指定需要异步验证的字段
})(AsyncValidationForm);

asyncValidate.js:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const asyncValidate = (values /*, dispatch */) => {
    return sleep(1000).then(() => { // simulate server latency
        if ([‘john‘, ‘paul‘, ‘george‘, ‘ringo‘].includes(values.username)) {
            throw { username: ‘That username is taken‘ };    //该用户名已被使用
        }
    });
};

注:throw {} 抛出错误对象,只有在reduxForm()中定义了指定异步验证的字段才能捕获(caught)

如下图:即当同步验证密码输入值后失去焦点不会触发异步验证

[6.Submit 提交及 Submit Validation]

Form 组件对React的form组件进行了简单的封装,用以触发用 redux-form 修饰的组件的 onSubmit 函数。

在您表单组件内部,可以通过 onSubmit={this.props.handleSubmit(this.mySubmitFunction)} 执行您的提交。

如:示例代码

submit.js:
function submit(values) {
    return sleep(1000).then(() => { // simulate server latency 模拟服务器延迟
        if (![‘john‘, ‘paul‘, ‘george‘, ‘ringo‘].includes(values.username)) {
            throw new SubmissionError({
                username: ‘User does not exist‘,
                _error: ‘Login failed!‘,
            });
        } else if (values.password !== ‘redux-form‘) {
            throw new SubmissionError({
                password: ‘Wrong password‘,
                _error: ‘Login failed!‘,
            });
        } else {
            window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`);
        }
    });
}

import submit from ‘./submit‘;
const SubmitValidationForm = props => {
    const { error, handleSubmit, pristine, reset, submitting } = props;
    return (
        <form onSubmit={handleSubmit(submit)}>
            <Field
                name="username"
                type="text"
                component={renderField}
                label="Username"
            />
            <Field
                name="password"
                type="password"
                component={renderField}
                label="Password"
            />
            {error && <strong>{error}</strong>}
            <div>
                <button type="submit" disabled={submitting}>Log In</button>
                <button type="button" disabled={pristine || submitting} onClick={reset}>
                    Clear Values
                </button>
            </div>
        </form>
    );
};

注:

这个 SubmissionError 用于从 onSubmit 返回一个表单验证错误信息。目的是用来区分 promise 失败的原因究竟是验证错误、AJAX I/O错误还是其他服务器错误。

如果它是由于表单里 { field1: ‘error‘, field2: ‘error‘ }产生的错误,那这个错误将会被添加到每一个标记过错误属性的字段里,就像异步表单验证错误一样。

如果有一个错误没有指定的字段,但又适用于整个表单,你可以将此错误信息指定到 _error 字段并将它作为error prop传递到整个表单

【props.error】

在同步验证功能结果中,整个表单一般的错误(即props.error错误信息)由 _error key设定。

如代码:const { error } = props,设置 _error = ‘报错啦‘,那么代码中的 error = ‘报错啦‘

时间: 2024-10-12 07:55:48

Redux-Form 基础使用的相关文章

form(form基础、标签渲染)

form基础 Django中的Form使用时一般有两种功能: 1.生成html标签 2.验证输入内容 要想使用django提供的form,要在views里导入form模块 from django import forms 首先我们创建我们的模版 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</ti

Django学习系列之Form基础

Form对与一个网站来说就是一个灵魂,今天我们就来说说Django的Form概述. 本文内容来自官网,互联网及自己实验所得. 表单(forms.py)的定义: #coding:utf-8from django import formsfrom django.forms.extras.widgets import SelectDateWidget SEX_CHOICES=(('male','男'),('female','女')) BIRTH_YEAR_CHOICES = ('1980', '198

Redux 的基础概念-API

三个基本原则 整个应用只有唯一一个可信数据源,也就是只有一个 Store State 只能通过触发 Action 来更改 State 的更改必须写成纯函数,也就是每次更改总是返回一个新的 State,在 Redux 里这种函数称为 Reducer Actions Action 很简单,就是一个单纯的包含 { type, payload } 的对象,type 是一个常量用来标示动作类型,payload 是这个动作携带的数据.Action 需要通过 store.dispatch() 方法来发送. 比

jQuery基础知识--Form基础(续)

下拉框应用 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="../../js/jquery-2.1.3.js"></script> <title></title> <style

实现同时提交多个form(基础方法) 收集(转)

方法一: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=g

【共享单车】—— React后台管理系统开发手记:AntD Form基础组件

前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. 一.使用Form组件开发登录页面 pages->form->login.js:对应路由/admin/form/login import React from 'react' import {Card, Form, Input, Button, message, Icon, Checkbox} f

redux 基础

什么时候用? 某个组件的状态,需要共享 某个状态需要在任何地方都可以拿到 一个组件需要改变全局状态 一个组件需要改变另一个组件的状态 你用react不能解决的组件通信,redux可以给你解决. redux核心概念 store相当于一个应用的state,但是不可随意更改,必须通过action更改. 强制使用 action 来描述所有变化带来的好处是可以清晰地知道应用中到底发生了什么. 为了把 action 和 state 串起来,开发一些函数,这就是 reducer. reducer 只是一个接收

通俗易懂的理解 Redux(知乎)

1. React有props和state: props意味着父级分发下来的属性[父组件的state传递给子组件  子组件使用props获取],state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,也就是说数据只能单向向下分发,或者自行内部消化.理解这个是理解React和Redux的前提.2. 一般构建的React组件内部可能是一个完整的应用,它自己工作良好,你可以通过属性作为API控制它.但是更多的时候发现React根本无法让两个组件互相交流,使用对方的数据.然后

Bootstrap系列 -- 11. 基础表单

表单主要功能是用来与用户做交流的一个网页控件,良好的表单设计能够让网页与用户更好的沟通.表单中常见的元素主要包括:文本输入框.下拉选择框.单选按钮.复选按钮.文本域和按钮等.其中每个控件所起的作用都各不相同,而且不同的浏览器对表单控件渲染的风格都各有不同. <form role="form"> <div class="form-group"> <label for="exampleInputEmail1">邮箱

flux,redux,vuex状态集管理工具之间的区别

一:redux和flux的区别 1)redux是flux中的一个实现 2))在redux中我们只能定义一个store,在flux中我们可以定义多个 3)在redux中,store和dispatch都放到了store,结构更加清晰 4)在redux中本身就内置State对象,对仓库的管理更加明确 二:redux和vuex的区别 1)vuex是redux的基础上进行改变,对仓库的管理更加明确 2)使用mutation来替换redux中的reducer 3)vuex有自动渲染的功能,所以不需要更新 三