您的当前位置:首页ReactForm组件封装步骤详解

ReactForm组件封装步骤详解

2023-11-30 来源:华拓教育
这次给大家带来React Form组件封装步骤详解,React Form组件封装的注意事项有哪些,下面就是实战案例,一起来看一下。

前言

对于网页系统来说,表单提交是一种很常见的与用户交互的方式,比如提交订单的时候,需要输入收件人、手机号、地址等信息,又或者对系统进行设置的时候,需要填写一些个人偏好的信息。 表单提交是一种结构化的操作,可以通过封装一些通用的功能达到简化开发的目的。本文将讨论Form表单组件设计的思路,并结合有赞的ZentForm组件介绍具体的实现方式。本文所涉及的代码都是基于React v15的版本。

Form组件功能

一般来说,Form组件的功能包括以下几点:

  1. 表单布局

  2. 表单字段

  3. 封装表单验证&错误提示

  4. 表单提交

下面将对每个部分的实现方式做详细介绍。

表单布局

常用的表单布局一般有3种方式:

行内布局

水平布局

垂直布局

实现方式比较简单,嵌套css就行。比如form的结构是这样:

<form class="form"> <label class="label"/> <field class="field"/></form>

对应3种布局,只需要在form标签增加对应的class:

<!--行内布局--><form class="form inline"> <label class="label"/> <field class="field"/></form><!--水平布局--><form class="form horizontal"> <label class="label"/> <field class="field"/></form><!--垂直布局--><form class="form vertical"> <label class="label"/> <field class="field"/></form>

相应的,要定义3种布局的css:

.inline .label { display: inline-block; ...}.inline .field { display: inline-block; ...}.horizontal .label { display: inline-block; ...}.horizontal .field { display: inline-block; ...}.vertical .label { display: block; ...}.vertical .field { display: block; ...}

表单字段封装

字段封装部分一般是对组件库的组件针对Form再做一层封装,如Input组件、Select组件、Checkbox组件等。当现有的字段不能满足需求时,可以自定义字段。

表单的字段一般包括两部分,一部分是标题,另一部分是内容。ZentForm通过getControlGroup这一高阶函数对结构和样式做了一些封装,它的入参是要显示的组件:

export default Control => { render() { return ( <p className={groupClassName}> <label className="zent-formcontrol-label"> {required ? <em className="zent-formrequired">*</em> : null} {label} </label> <p className="zent-formcontrols"> <Control {...props} {...controlRef} /> {showError && ( <p className="zent-formerror-desc">{props.error}</p> )} {notice && <p className="zent-formnotice-desc">{notice}</p>} {helpDesc && <p className="zent-formhelp-desc">{helpDesc}</p>} </p> </p> ); }}

这里用到的label和error等信息,是通过Field组件传入的:

<Field label="预约门店:" name="dept" component={CustomizedComp} validations={{ required: true, }} validationErrors={{ required: '预约门店不能为空', }} required/>

这里的CustomizedComp是通过getControlGroup封装后返回的组件。

字段与表单之间的交互是一个需要考虑的问题,表单需要知道它包含的字段值,需要在适当的时机对字段进行校验。ZentForm的实现方式是在Form的高阶组件内维护一个字段数组,数组内容是Field的实例。后续通过操作这些实例的方法来达到取值和校验的目的。

ZentForm的使用方式如下:

class FieldForm extends React.Component { render() { return ( <Form> <Field name="name" component={CustomizedComp} </Form> ) }}export default createForm()(FieldForm);

其中Form和Field是组件库提供的组件,CustomizedComp是自定义的组件,createForm是组件库提供的高阶函数。在createForm返回的组件中,维护了一个fields的数组,同时提供了attachToForm和detachFromForm两个方法,来操作这个数组。这两个方法保存在context对象当中,Field就能在加载和卸载的时候调用了。简化后的代码如下:

/** * createForm高阶函数 */const createForm = (config = {}) => { ... return WrappedForm => { return class Form extends Component { constructor(props) { super(props); this.fields = []; } getChildContext() { return { zentForm: { attachToForm: this.attachToForm, detachFromForm: this.detachFromForm, } } } attachToForm = field => { if (this.fields.indexOf(field) < 0) { this.fields.push(field); } }; detachFromForm = field => { const fieldPos = this.fields.indexOf(field); if (fieldPos >= 0) { this.fields.splice(fieldPos, 1); } }; render() { return createElement(WrappedForm, {...}); } } }}/** * Field组件 */class Field extends Component { componentWillMount() { this.context.zentForm.attachToForm(this); } componentWillUnmount() { this.context.zentForm.detachFromForm(this); } render() { const { component } = this.props; return createElement(component, {...}); }}

当需要获取表单字段值的时候,只需要遍历fields数组,再调用Field实例的相应方法就可以:

/** * createForm高阶函数 */const createForm = (config = {}) => { ... return WrappedForm => { return class Form extends Component { getFormValues = () => { return this.fields.reduce((values, field) => { const name = field.getName(); const fieldValue = field.getValue(); values[name] = fieldValue; return values; }, {}); }; } }}/** * Field组件 */class Field extends Component { getValue = () => { return this.state._value; };}

表单验证&错误提示

表单验证是一个重头戏,只有验证通过了才能提交表单。验证的时机也有多种,如字段变更时、鼠标移出时和表单提交时。ZentForm提供了一些常用的验证规则,如非空验证,长度验证,邮箱地址验证等。当然还能自定义一些更复杂的验证方式。自定义验证方法可以通过两种方式传入ZentForm,一种是通过给createForm传参:

createForm({ formValidations: { rule1(values, value){ }, rule2(values, value){ }, }})(FormComp);

另一种方式是给Field组件传属性:

<Field validations={{ rule1(values, value){ }, rule2(values, value){ }, }} validationErrors={{ rule1: 'error1', rule2: 'error2' }}/>

使用createForm传参的方式,验证规则是共享的,而Field的属性传参是字段专用的。validationErrors指定校验失败后的提示信息。这里的错误信息会显示在前面getControlGroup所定义HTML中{showError && (<p className="zent-formerror-desc">{props.error}</p>)}

ZentForm的核心验证逻辑是createForm的runRules方法,

runRules = (value, currentValues, validations = {}) => { const results = { errors: [], failed: [], }; function updateResults(validation, validationMethod) { // validation方法可以直接返回错误信息,否则需要返回布尔值表明校验是否成功 if (typeof validation === 'string') { results.errors.push(validation); results.failed.push(validationMethod); } else if (!validation) { results.failed.push(validationMethod); } } Object.keys(validations).forEach(validationMethod => { ... // 使用自定义校验方法或内置校验方法(可以按需添加) if (typeof validations[validationMethod] === 'function') { const validation = validations[validationMethod]( currentValues, value ); updateResults(validation, validationMethod); } else { const validation = validationRules[validationMethod]( currentValues, value, validations[validationMethod] ); } }); return results;};

默认的校验时机是字段值改变的时候,可以通过Field的validateOnChangevalidateOnBlur来改变校验时机。

<Field validateOnChange={false} validateOnBlur={false} validations={{ required: true, matchRegex: /^[a-zA-Z]+$/ }} validationErrors={{ required: '值不能为空', matchRegex: '只能为字母' }}/>

对应的,在Field组件中有2个方法来处理change和blur事件:

class Field extends Component { handleChange = (event, options = { merge: false }) => { ... this.setValue(newValue, validateOnChange); ... } handleBlur = (event, options = { merge: false }) => { ... this.setValue(newValue, validateOnBlur); ... } setValue = (value, needValidate = true) => { this.setState( { _value: value, _isDirty: true, }, () => { needValidate && this.context.zentForm.validate(this); } ); };}

当触发验证的时候,ZentForm是会对表单对所有字段进行验证,可以通过指定relatedFields来告诉表单哪些字段需要同步进行验证。

表单提交

表单提交时,一般会经历如下几个步骤

  1. 表单验证

  2. 表单提交

  3. 提交成功处理

  4. 提交失败处理

ZentForm通过handleSubmit高阶函数定义了上述几个步骤,只需要传入表单提交的逻辑即可:

const handleSubmit = (submit, zentForm) => { const doSubmit = () => { ... result = submit(values, zentForm); ... return result.then( submitResult => { ... if (onSubmitSuccess) { handleOnSubmitSuccess(submitResult); } return submitResult; }, submitError => { ... const error = handleSubmitError(submitError); if (error || onSubmitFail) { return error; } throw submitError; } ); } const afterValidation = () => { if (!zentForm.isValid()) { ... if (onSubmitFail) { handleOnSubmitError(new SubmissionError(validationErrors)); } } else { return doSubmit(); } }; const allIsValidated = zentForm.fields.every(field => { return field.props.validateOnChange || field.props.validateOnBlur; }); if (allIsValidated) { // 不存在没有进行过同步校验的field afterValidation(); } else { zentForm.validateForm(true, afterValidation); }}

使用方式如下:

const { handleSubmit } = this.props;<Form onSubmit={handleSubmit(this.submit)} horizontal>

ZentForm不足之处

ZentForm虽然功能强大,但仍有一些待改进之处:

  1. 父组件维护了所有字段的实例,直接调用实例的方法来取值或者验证。这种方式虽然简便,但有违React声明式编程和函数式编程的设计思想,并且容易产生副作用,在不经意间改变了字段的内部属性。

  2. 大部分的组件重使用了shouldComponentUpdate,并对state和props进行了深比较,对性能有比较大的影响,可以考虑使用PureComponent。

  3. 太多的情况下对整个表单字段进行了校验,比较合理的情况应该是某个字段修改的时候只校验本身,在表单提交时再校验所有的字段。

  4. 表单提交操作略显繁琐,还需要调用一次handleSubmit,不够优雅。

结语

本文讨论了Form表单组件设计的思路,并结合有赞的ZentForm组件介绍具体的实现方式。ZentForm的功能十分强大,本文只是介绍了其核心功能,另外还有表单的异步校验、表单的格式化和表单的动态添加删除字段等高级功能都还没涉及到,感兴趣的朋友可点击前面的链接自行研究。

相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

推荐阅读:

JS callback回调函数使用案例详解

React Navigation实战中有哪些注意事项

小编还为您整理了以下内容,可能对您也有帮助:

react如何实现多个表单下一步和上一步

实现多个表单下一步和上一步可以通过以下步骤实现:

1、创建一个父组件,用于管理所有表单数据和状态,以及渲染不同的子表单组件。

2、在父组件中定义一个状态currentStep,用于表示当前显示的表单索引。

3、在父组件中定义一个方法nextStep和prevStep,用于切换当前表单的索引,实现下一步和上一步的操作。

4、在父组件中定义一个方法handleSubmit,用于提交表单数据。

5、在子表单组件中,定义表单元素和事件处理程序。

6、在子表单组件中,定义一个方法handleNext,用于调用父组件的nextStep方法,切换到下一个表单。

7、在子表单组件中,定义一个方法handlePrev,用于调用父组件的prevStep方法,切换到上一个表单。

8、在最后一个子表单中,定义一个提交按钮,用于提交表单数据,调用父组件的handleSubmit方法。

formik中文文档

一、简介:

1、什么是Formik:是由React组件和hooks组成,它内置了表单的state管理操作,无需我们在单独为表单建立state,同时使用了Context,能够让表单组件多层嵌套,不再需要我们一层层传递。

2、解决哪些问题:

二、起步

1、安装

2、推荐的校验库 Yup

学习文档推荐: https://www.hu.com/search?type=content&q=Yup%20js

官方文档: https://github.com/jquense/yup

3、可以结合第三方UI库一起使用

4、Formik提交流程

提交前:

校验:

提交:

5、校验

https://formik.org/docs/guides/validation , 这里写的很详细,标明了何时会触发校验

三、组件用法

1、<Field />

as :

input

select

textarea

有效的HTML元素名

React组件

children : 是个react组件, 接收这3个参数

component :接收一个组件,( { field, form, ...props }) => ()

innerRef :回调函数,接收一个DOM节点

name : 字段名称,name也可以接受类似lodash的点路径,例如:

2、<FieldArray />

渲染子节点方法:

3、<ErrorMessage />

children?: ((message: string) => React.ReactNode)

component?: string | React.ComponentType<FieldProps>

name: string

render?: (error: string) => React.ReactNode

4、<Form />

Form组件都干了啥?

1、在<form>标签基础上进行封装

2、接收Formik传下来的参数,把form标签自带的方法传进去

5、<FastField />

<FastField /> 是 <Field /> 用于大型表单(〜30多个字段)或字段具有非常昂贵的验证要求的优化版本。 <FastField /> 具有与完全相同的API <Field> ,但在 shouldComponentUpdate() 内部实现以阻止所有其他重新渲染,除非 <FastField /> 对 Formik 状态的的相关部分/片段进行了直接更新。如果您的表单中的<Field />与其他所有 <Field /> 表单“无关” ,则可以使用 <FastField /> ,它不依赖于顶级其他部分 <Formik /> 的状态。

6、<Formik />

四、hooks

1、useField

2、useFormikContext

3、useFormik

五、方法:

1、connect

将传进来的组件,传个formik对象,共享formik里的属性和方法

六、Yup的优点缺点

优点:功能强大

缺点:

1、复用性不强,因为他是链式结构

2、一坨,没有json-schema看起来简单直接

React使用Ant Design Mobile结合rc-form进行表单验证

rc-form是什么:

在使用Ant Design Mobile的InputItem文本输入时提示"受控组件建议使用rc-form",那么这个rc-from究竟是什么呢?

答:rc-form是一个将输入框变为受控组件的库,也是一个React的高阶form组件。它集合了对于输入框的各种方法,可以使我们方便的控制输入框的状态。

使用方法

首先安装并导入rc-form库引入createForm方法,然后使用createForm()在组件'CountSet'上挂载form对象,即在当前组件的props上挂在了form,通过使用props.form.相关方法,即可调用rc-form中定义的方法。

API:

validateFields( [fieldNames: string[]],[options: object],*callback(errors, values)******) => void *****校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件。

getFieldsValue([fieldNames: string[]]* ) 获取一组控件的值,若 fieldNames 参数为空,则获取全部组件。*

getFieldsError([names: string[]]* ) 获取一组输入控件的 Error ,如不传入参数,则获取全部组件的 Error。*

getFieldProps( name,options) 用于为控件绑定名称(key),及控件的一些默认配置。其中options参数简单说两个 initialValue和 rules,initialValue是控件的默认初始值绑定在name上;rules是对输入框的内容做一些*,比如是否必须输入(require),最大(max)最小(min)长度等,也可以自定义*(validator:function(rule, value, callback))。

一个例子:

React使用Ant Design Mobile结合rc-form进行表单验证

rc-form是什么:

在使用Ant Design Mobile的InputItem文本输入时提示"受控组件建议使用rc-form",那么这个rc-from究竟是什么呢?

答:rc-form是一个将输入框变为受控组件的库,也是一个React的高阶form组件。它集合了对于输入框的各种方法,可以使我们方便的控制输入框的状态。

使用方法

首先安装并导入rc-form库引入createForm方法,然后使用createForm()在组件'CountSet'上挂载form对象,即在当前组件的props上挂在了form,通过使用props.form.相关方法,即可调用rc-form中定义的方法。

API:

validateFields( [fieldNames: string[]],[options: object],*callback(errors, values)******) => void *****校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件。

getFieldsValue([fieldNames: string[]]* ) 获取一组控件的值,若 fieldNames 参数为空,则获取全部组件。*

getFieldsError([names: string[]]* ) 获取一组输入控件的 Error ,如不传入参数,则获取全部组件的 Error。*

getFieldProps( name,options) 用于为控件绑定名称(key),及控件的一些默认配置。其中options参数简单说两个 initialValue和 rules,initialValue是控件的默认初始值绑定在name上;rules是对输入框的内容做一些*,比如是否必须输入(require),最大(max)最小(min)长度等,也可以自定义*(validator:function(rule, value, callback))。

一个例子:

react表单和绑定事件及state和props-04

第一种方式:在事件上使用bind(this)

第二种方式:在构造函数中改变this指向

第三种方式:使用=()=>

JavaScript函数中的this不是在函数声明的时候定义的,而是在函数调用(即运行)的时候定义的。在react中也遵循这个原则,this指向该function的调用方,所以“调用者”不同会导致this的不同,这里的“调用者”指的是函数执行时的当前对象。

箭头函数或是bind会改变this的指向。上面的this.state,之所以能够访问到state就是改变了this的指向,不然getData里的this就是指向这个function,这是的this是undefined。

一般来说:
    1、谁调用的this就指向谁。
    2、箭头函数,bind改变后的指向父层。

在react中我们使用setState来更新数据:

但是console打印出来的值是:原数据。
这里就要提到react里setState的异步了,在react里setState这个方法是异步的,不会按顺序执行,它可能会等待一组state一起进行更新,来提高性能。下面提供一个解决办法,setState的回调setState(updater[, callback]),相当于componentDidUpdate函数:

因为在事件中使用onClick={this.setData()}时方法直接执行,直接写成this.setData(data)这样会报错,所以需要使用bind():

下面看如何传递:

事件对象:在出发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。

给标签添加ref属性,在事件里通过this.refs.[属性].value来获取值

双向数据绑定在vue中有,在react中是没有的,双向数据绑定就是双方的数据改变相互影响,下面只是模拟双向数据绑定:

上面的的value需要事件监听,这里面有一个react的知识点:约束性组件和非约束性组件
约束性组件,简单说就是react管理了它的value,而非约束性组件的value是由原生DOM管理的。

约束性组件是这样写的:

这里的value属性不再是一个写死的值,它是this.state.data,而this.state.data是由this.inputDataChange这个监听事件负责管理的。
这个时候实际上 input 的 value 根本不是用户输入的内容。而是onChange 事件触发之后,由 this.setState 导致了一次重新渲染。不过React会优化这个渲染过程,实际它依然是通过设置input的value来实现的。

非约束性组件是这样写的:

这个defaultValue就是原生DOM中的value属性,其value值就是用户输入的内容,react不管理输入的过程。

对比约束性组件和非约束性组件的输入流程:
*约束性组件: 用户输入A -> 触发onChange事件 -> inputDataChange 中设置 state.name = “A” -> 渲染input使他的value变成A
*非约束性组件: 用户输入A -> input 中显示A

这里介绍form表单里的几种常用的子标签:
一般表单都是需要用form标签包起来:

1、react的组件从概念上看就是一个函数,可以接受一个参数最为输入值,这个参数就是props,可以把props理解为从外部传入组件内部的数据。
2、props经常被用作渲染组件和初始化状态,当一个组件被实例化之后,它的props是只读的,不可改变的。如果props在渲染过程中可以被改变,会导致这个组件显示的形态变得不可预测。只有通过父组件重新渲染的方式才可以把新的props传入组件中。
3、在组件中,我们最好为props中的参数设置一个defaultProps,并且制定它的类型。

一个组件的显示形态可以由数据状态和外部参数所决定,外部参数也就是props,而数据状态就是state
state的主要作用是用于组件保存、控制以及修改自己的状态,它只能在constructor中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的this.setState来修改,修改state属性会导致组件的重新渲染。

写在最后:

react表单和绑定事件及state和props-04

第一种方式:在事件上使用bind(this)

第二种方式:在构造函数中改变this指向

第三种方式:使用=()=>

JavaScript函数中的this不是在函数声明的时候定义的,而是在函数调用(即运行)的时候定义的。在react中也遵循这个原则,this指向该function的调用方,所以“调用者”不同会导致this的不同,这里的“调用者”指的是函数执行时的当前对象。

箭头函数或是bind会改变this的指向。上面的this.state,之所以能够访问到state就是改变了this的指向,不然getData里的this就是指向这个function,这是的this是undefined。

一般来说:
    1、谁调用的this就指向谁。
    2、箭头函数,bind改变后的指向父层。

在react中我们使用setState来更新数据:

但是console打印出来的值是:原数据。
这里就要提到react里setState的异步了,在react里setState这个方法是异步的,不会按顺序执行,它可能会等待一组state一起进行更新,来提高性能。下面提供一个解决办法,setState的回调setState(updater[, callback]),相当于componentDidUpdate函数:

因为在事件中使用onClick={this.setData()}时方法直接执行,直接写成this.setData(data)这样会报错,所以需要使用bind():

下面看如何传递:

事件对象:在出发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。

给标签添加ref属性,在事件里通过this.refs.[属性].value来获取值

双向数据绑定在vue中有,在react中是没有的,双向数据绑定就是双方的数据改变相互影响,下面只是模拟双向数据绑定:

上面的的value需要事件监听,这里面有一个react的知识点:约束性组件和非约束性组件
约束性组件,简单说就是react管理了它的value,而非约束性组件的value是由原生DOM管理的。

约束性组件是这样写的:

这里的value属性不再是一个写死的值,它是this.state.data,而this.state.data是由this.inputDataChange这个监听事件负责管理的。
这个时候实际上 input 的 value 根本不是用户输入的内容。而是onChange 事件触发之后,由 this.setState 导致了一次重新渲染。不过React会优化这个渲染过程,实际它依然是通过设置input的value来实现的。

非约束性组件是这样写的:

这个defaultValue就是原生DOM中的value属性,其value值就是用户输入的内容,react不管理输入的过程。

对比约束性组件和非约束性组件的输入流程:
*约束性组件: 用户输入A -> 触发onChange事件 -> inputDataChange 中设置 state.name = “A” -> 渲染input使他的value变成A
*非约束性组件: 用户输入A -> input 中显示A

这里介绍form表单里的几种常用的子标签:
一般表单都是需要用form标签包起来:

1、react的组件从概念上看就是一个函数,可以接受一个参数最为输入值,这个参数就是props,可以把props理解为从外部传入组件内部的数据。
2、props经常被用作渲染组件和初始化状态,当一个组件被实例化之后,它的props是只读的,不可改变的。如果props在渲染过程中可以被改变,会导致这个组件显示的形态变得不可预测。只有通过父组件重新渲染的方式才可以把新的props传入组件中。
3、在组件中,我们最好为props中的参数设置一个defaultProps,并且制定它的类型。

一个组件的显示形态可以由数据状态和外部参数所决定,外部参数也就是props,而数据状态就是state
state的主要作用是用于组件保存、控制以及修改自己的状态,它只能在constructor中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的this.setState来修改,修改state属性会导致组件的重新渲染。

写在最后:

在React中如何实现组件内部通信

这篇文章主要介绍了React数据传递之组件内部通信的方法,现在分享给大家,也给大家做个参考。

1. 概述

脱离初级前端一段时间后会发现,写样式的时间越来越少,处理数据的时间越来越多。处理数据的过程也就是实现业务逻辑的过程,这在项目中无疑是最重要的。

所以学习前端框架,了解完基本语法后,接下来就要学习其如何进行数据传递。

Angular 设计之初的一大亮点就是实现了数据的双向绑定,使用 Vue 一段时间后发现,所谓数据的双向绑定,组件内部唯一的应用场景就是 form 表单(input,textarea,select, radio),而这种场景下的数据双向绑定,即便框架内部没有实现,自己实现起来也非常简单。明白这一点后感觉之前认为 React 没有实现数据双向绑定很 low 的想法很幼稚。

对于 React 的数据传递,涉及两方面的内容:

组件内部的数据传递,典型的应用场景包括如何实现 form 表单双向数据绑定、如何绑定事件;

组件间的数据传递。 包括父组件往子组件传递数据、子组件往父组件传递数据以及兄弟组件之间传递数据。

本文先讨论组件内部的数据传递。

2. 组件内部数据传递

React 组件内部通信主要分为两部分:数据展示与事件处理。

2.1 数据展示

组件内部数据的展示和更新都是通过 state 来实现的,如果要使用 state 必须使用 ES6 的 class 定义组件。数据更新在双向数据绑定部分探讨,这部分仅讨论展示初始化数据。

如果你熟悉 Vue,React 的 state 对象相当于 Vue 的 data 对象

下面是一个纯展示数据的示例:

class App extends Component {

constructor(props) {

super(props);

// 初始化 state

this.state = {

inputValue: "test",

};

}

render() {

// 注意,在 react 中,DOM 元素是对象,所以使用‘()'包住

return (

<p className="App">

<p>{this.state.inputValue}</p>

</p>

);

}

}在通过 class 定义的 React 组件中,除了生命周期钩子函数, constructor() 和 render() 着两个方法也是自动执行的,先执行 constructor() ,执行 constructor() 的同时也是再为 render() 渲染 DOM 做数据准备。

实际上 constructor() 函数是组件生命周期中调用的第一个函数。

2.2 事件

2.2.1 与 DOM 中事件的异同

在 React 中处理事件和在 DOM 中处理事件类似,有两点不同:

React 中通过驼峰命名法命名事件,而不是全是小写字母;

在 JSX 中直接传递函数作为事件处理程序,而不是字符串。

第 2 点不同有坑,后面细说

举个例子,HTML中的事件:

<button onclick="activateLasers()">

Activate Lasers

</button>React 中的事件:

// 因为 jsx 中'{}'里面代表函数表达式,

// 所以传递给 onClick 的实际是函数 activateLasers 的函数体部分,

// 因此需要指定 this 指向,不然会报错

<button onClick={activateLasers}>

Activate Lasers

</button>2.2.2 存在的坑

直接传递 function 作为 event handler 需要指定函数的执行环境,即需要手动绑定 this ,不然会报 this 为 undefined 的错。见下面的例子:

class App extends Component {

constructor(props) {

super(props);

this.state = {

isToggleOn: true,

};

// 手动绑定 this

this.handleClick = this.handleClick.bind(this);

}

handleClick() {

// 如果不在 constructor() 方法中手动绑定 this,直接将其作为事件处理程序 this 为 undefined

console.log(this);

this.setState(prevState => ({

isToggleOn: !prevState.isToggleOn

}));

}

render() {

return (

<p className="App">

<button onClick={this.handleClick}>

{this.state.isToggleOn ? "on" : "off"}

</button>

</p>

);

}

}2.2.3 为什么会有坑

React 官网 说这个锅要 JS 原生语法来背,其实不尽然,React 实在 JS 语法早已确定的情况下设计了这样的事件系统,如果一定要有人站出来背锅,他们五五分吧。

1, JS原生语法存在的问题

JS语法中有这样的规则:如果将一个函数的函数体(没有 () )赋值给另一个变量,函数体内部的 this 指向可能会发生变化。会不会变化取决于函数和被赋值的变量是否处于同一个作用域(相同的执行环境)中,但实际使用中,将一个函数赋值给相同作用域的变量没有意义,那样的话直接使用那个函数就好,没必要在赋值给另一个变量。

this 指向不发生改变的没有意义的例子(为了方便说明,直接使用 var 操作符):

var fn = function () {

console.log(this);

};

var a = fn;

fn(); // window

a(); // window

this 指向发生改变的例子:

var fn = function () {

console.log(this);

};

// 将函数体赋值给一个对象的属性,函数执行时 this 和定义时指向不同

var o = {

a: fn,

};

fn(); // window

o.a(); // o,即{a:f}如果想要在将函数体赋值另一个变量的同时把原函数的 this 指向也一块赋值过去,就需要在赋值的过程中进行绑定 this 的操作,如下:

var fn = function () {

console.log(this);

};

// fn 在赋值的同时将内部的 this 打包一块赋值给了 a

var o = {

a: fn.bind(this),

};

fn(); // window

o.a(); // window通常在将函数体赋值给变量的时候为了避免 this 出错,都会进行 绑定执行环境的操作 ,典型的例子是 var bindId = document.getElementById.bind(document)

2, JSX 存在的问题

因为 JSX 中 DOM 元素也是对象,给元素的属性赋值实际是给 DOM 元素对象的属性赋值,见下:

const element = (

<button onClick={this.handleClick}>click me</button>

);等同于

const element = {

type: 'button',

props: {

onClick: this.handleClick,

children: 'click me',

},

};

这实际就是 将函数体赋值给一个对象的属性,函数执行时 this 和定义时指向不同 的场景,和原生语法相同的是 this 指向发生了改变,不同的是原生 JS 中不管怎样, this 总归是有个指向的,而 JSX 直接 undefined 。

所以说不绑定 this 报 undefined 的错不能全怪 JS 原生语法。

3. 双向数据绑定

通过 state 传递数据加上事件处理程序便能实现数据的双向绑定,其背后的思想是(以 input 为例):初始化时将 state 中预定义的 state a 赋值给 input,当 input 的 value 发生改变时,触发事件处理程序,将改变后的 value 赋值给状态 a ,React 监测到 state 改变时重新调用 render() 方法,即重新渲染组件,达到双向绑定的目的。

class App extends Component {

constructor(props) {

super(props);

this.state = {

inputValue: "test",

};

this.changeInput = this.changeInput.bind(this);

}

changeInput(e) {

// 将改变后的 input 值赋值给 inputValue,通过事件对象 $event.target.value 实现

this.setState({

inputValue: e.target.value

});

}

render() {

// input 改变时触发 changeInput

return (

<p className="App">

<input value={this.state.inputValue} onChange={this.changeInput} />

<p>{this.state.inputValue}</p>

</p>

);

}

}这里用到了事件对象,React 的事件对象和 JS 原生事件对象保持一致。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

使用Node.js爬虫如何实现网页请求

使用VueAwesomeSwiper容易出现的问题?

在angular2中有关Http请求原理(详细教程)

在node中如何实现http小爬虫

【React】antd的form表单的自定义校验规则的用法

在用到antd的Form组件的时候,可能会用到自定义的规则,以我的项目为例:在输入名称的时候需要请求接口,校验的内容是后台返回的内容,所以这个时候需要用到自定义的校验规则

那么具体的写法如下:将validator放在rules的数组中

调用的方法如下:当value在变化的时候请求接口,通过接口返回的值传给callback回调函数

具体可以参考GitHub上的这篇文档: https://github.com/ant-design/ant-design/issues/5155

本文如未解决您的问题请添加抖音号:51dongshi(抖音搜索懂视),直接咨询即可。