博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ReactJS学习笔记——组件复合及表单的处理
阅读量:7193 次
发布时间:2019-06-29

本文共 10164 字,大约阅读时间需要 33 分钟。

hot3.png

#ReactJS学习笔记——组件复合及表单的处理

React是一个JavaScript库文件,使用它的目的在于能够解决构建大的应用和数据的实时变更。该设计使用JSX允许你在构建标签结构时充分利用JavaScript的强大能力,而不必在笨拙的模板语言上浪费时间。

系统环境:window x86_64

命令行工具:git-bash

React版本:React v0.14.7

##1 组件的复合 在传统的HTML中,元素是构成页面的基础单元。但是在React中,构建页面的基础单元是React组件。你可以把React组件理解成为混入了JavaScript表达能力的HTML元素。实际上写React代码就是构建组件,就像编写HTML文档时使用元素一样。 本质上,一个组件就是一个JavaScript函数,它接受属性(props)和(state)作为参数,并输出渲染好的HTML。组件一般被用来呈现和表达应用的某部分数据,因此你可以把React组件理解为HTML元素的拓展。 React+JSX是强大而富有表现力的工具,允许我们使用类似HTML的语法创建自定义元素,比起单纯的HTML,他们还能够控制僧命周期中的行为。这些都在从React.createClass方法开始的。相较于继承ES6已经开始支持,实现多个小巧、简单的组件和数据对象,构造成大而复杂的组件。

###1.1 组件复合的例子

  1. 需求:做一个渲染选择题的组件
  2. 实现条件:(1)接收一组选项作为输入;(2)把选项渲染给用户;(3)只允许用户选择一个选项;

HTML提供了一些基本的元素——单选类型是输入框和表单组(input group),可以在这里使用。组件的层级从上往下看是这样的: MultipleChoice - RadioInput - Input (type="radio") 从先往后的顺序。选择题组件MultipleChoice"有一个"单选框RadioInput,单选框RadioInput”有一个“输入框元素Input。这里组合模式(composition pattern)的特性。

###1.2 组装HTML单选框RadioInput 依照从下往上的设计规则,我们首先需要组装一个RadioInput组件,这个组件使用了通用的input,,将其精缩成与单选按钮行为一致的组件。 ####1.2.1 添加动态属性 我们知道input还没有内容是动态的,我们需要定义一些能够有父元素传递给单选框的一些属性。

  • 这个单选框代表什么值,也是它的显示内容?(必填)
  • 这个单选框的name是什么?(必填)
  • 重载它的默认值,也是选择状态

####1.2.2 代码分析 RadioInput.js

var React = require('react');var uniqueId = require('lodash-node/modern/utility/uniqueId');var RadioInput = React.createClass({	// 1.添加动态属性	propTypes: {		name: React.PropTypes.string.isRequired,		value:React.PropTypes.string.isRequired,		checked:React.PropTypes.bool,		onChanged:React.PropTypes.func.isRequired	},	// 2.为非必要属性定义其默认值	getDefaultProps: function() {		return {			checked: false		}	},	// 3.追踪状态,组件需要记录随时间而变化的数据	getInitialState: function() {		var name = this.props.name ? this.props.name:uniqueId('radio-');		return {			checked: !!this.props.checked,			name: name		}	},	// 4.追踪当前组件的状态变更,并通过this.props.onChanged通知给父组件	handleChanged:function(e) {		var checked = e.target.checked;		this.setState({checked:checked});		if(checked) {			this.props.onChanged(this.props.value);		}	},	render:function() {		return(			
); }});module.exports = RadioInput;

代码总共分为5个步骤,这是绘制一个组件的基本流程:

  1. 定制单元模块所具有的属性,父元素能够通过动态属性传递数据到子组件,必需由父元素声明的属性加入isReauired,如果父元素没有对isReauired的属性进行声明,运行时会产生警告。RadioInput中必需的属性包含:name、value、onChanged(函数),具体类型声明参考:
  2. 对于非必要属性在getDefaultProps进行初始化。
  3. 追踪状态,在getInitialState中声明组件内的变量,记录者组件的数据变更,可以通过setState方法修改其内容。
  4. 这里不得不提一下onChange的处理函数handleChanged:function(e)(函数名可以自定义),在handleChanged:function(e)可以看到对属性函数onChanged方法this.props.onChanged(this.props.value);的调用,这里可以将组件的事件传递至父组件,由父组件相应当前子组件的变化。桥接了子组件和父组件之间的关系。
  5. 绘制当前组件,input的type为radio。

###1.3 父组件对子组件的整合

####1.3.1 设计思考 父组件期望组合一个单选组合,这一层的主要作用是渲染一列选项让用户从中选择。这里还是按照之前设计RadioInput的设计逻辑:

  1. 确定动态属性,当前单选组合选择内容:value,单选组合每个单选卡的内容:choices和点击完成的事件:onComplete
  2. 对于非必要属性在getDefaultProps进行初始化,这里不需要。
  3. 追踪状态,在getInitialState中声明组件内的变量,这里包含一个id和一个value。具体使用参考代码。
  4. 响应事件,发生事件时,调用setState并把事件对外传递通过this.props.onComplete
  5. render样式

####1.3.2 代码分析 MutileChoice.js

var React = require('react');var RadioInput = require('./RadioInput');var uniqueId = require('lodash-node/modern/utility/uniqueId');// 父组件var MutileChoice = React.createClass({	// 1.添加动态属性	propTypes: {		value: React.PropTypes.string,		choices: React.PropTypes.array.isRequired,		onComplete: React.PropTypes.func.isRequired	},	// 3.追踪状态,组件需要记录随时间而变化的数据	getInitialState: function() {		return {			id: uniqueId('multiple-choice-'),			value: this.props.value		}	},	// 4.响应事件	handleChanged: function(value) {		this.setState({value:value});		this.props.onComplete(value);	},	renderChoices: function() {		var SquareItemFactory = React.createFactory(RadioInput);		return this.props.choices.map(function(choice, i) {			// return AnswerRadioInput({			// 	id:"choice-"+i,			// 	name:this.state.id,			// 	label:choice,			// 	value:choice,			// 	checked:this.state.value === choice,			// 	onChanged: this.handleChanged			// });            return SquareItemFactory({				key:"choice-"+i,				name:this.state.id,				value:choice,				checked:this.state.value === choice,				onChanged: this.handleChanged            });		}.bind(this));	},	render: function() {		return(			
{this.renderChoices()}
); }});module.exports = MutileChoice;

代码中需要留意两个地方:(1)map使用;(2)注释代码中存在的问题。

  1. Array.prototype.map(),map是对array的每一个元素进行遍历,arr.map(callback[, thisArg])其中callback参数有三个(可选):currentValue:当前值,index当前元素索引,array当前数组;thisArg参数定义为:填入值为this,默认为window对象。

map参考

  1. 留意代码中注释掉的部分,直接构建子组件实例,没注释掉的部分使用工厂创建一个RadioInput实例,然后填入RadioInput实例的内容。参考如下代码:
renderChoices: function() {	var SquareItemFactory = React.createFactory(RadioInput);	return this.props.choices.map(function(choice, i) {		// return RadioInput({		// 	id:"choice-"+i,		// 	name:this.state.id,		// 	label:choice,		// 	value:choice,		// 	checked:this.state.value === choice,		// 	onChanged: this.handleChanged		// });		return SquareItemFactory({			key:"choice-"+i,			name:this.state.id,			value:choice,			checked:this.state.value === choice,			onChanged: this.handleChanged		});	}.bind(this));},

如果代码中,直接构建实例,在React v0.14.7环境下,会报出如下: Uncaught TypeError: Cannot read property '__reactAutoBindMap' of undefined

###1.4 使用已经封装好的组件 子组件和父组件都已经封装完毕,如何使用父组件来实现单选功能?这里只需要在实现代码中使用MutileChoice标签,同时定义相应的标签属性,代码如下所示:

var React = require('react');var ReactDOM = require("react-dom");var AnswerMutileChoice = require('./MutileChoice');var choices = [  "辽宁民间艺术团队", "开心麻花","贾玲团队","曹云金团队"];function handleComplete(value) {	console.log("handleComplete " + value);}ReactDOM.render(	
, document.body);

效果参考下图所示:

##2 表单使用 表单是应用必不可少的一部分,只要需要用户输入,哪怕是最简单的输入,都离不开表单。一直以来,单页应用的表单都很难处理好,因为表单充斥着用户变化莫测的状态,要管理好这些状态是很费神的,也很容易出现bug。React可以帮助你管理应用中的状态,自然也包括表单在内。 现在,你应该知道React组件的核心离你那就是可预知性和可测试性。给定同样的props和state,任何React组件都会渲染出一样的结果。表单也不例外。 在React中,表单有两种类型:约束组件和无约束组件。

###2.1 无约束组件 无约束表单的构造与React中大多数组件相比是反模式。在HTML中,表单组件与React组件行为并不一致。给定HTML的<input/>一个值,这个<input/>值仍是可以改变的。这正是无约束组件名称的由来,因为表单组件的值是不受React组件控制的。如果想访问它的值,需要给<input/>添加一个ref属性,以访问DOM节点的值。 ref是一个不属于DOM属性的特殊属性,用来标记DOM节点,可以通过this上下文访问这个节点。为了便于访问,组件的所有的ref都添加到了this.refs上。 下面我们在表单中添加一个<input/>,并在表单提交时访问它的值。

var React = require('react');var ReactDOM = require("react-dom");var MyForm = React.createClass({	submitHandler:function(event) {		event.preventDefault();		// 通过ref访问输入框		var helloTo = this.refs.helloTo.getDOMNode().value;		alert(helloTo);	},	render:function() {		return (			
); }});ReactDOM.render(
, document.body);

无约束组件可以用在基本的无需任何验证或者输入控制的表单中,当期望用户在输入的时候检测输入的变化的需要使用约束组件。

###2.2 约束组件 约束组件的模式与React其他类型的组件的模式一致。表单组件的状态交由React组件的控制,状态值被存储在React组件的state中。在约束组件中,输入框的值是由父组件设置的。我们对2.1中的代码进行改造,改成约束组件:

var MyForm = React.createClass({	// 1.定义默认值	getInitialState:function() {		return {			helloTo:"hello world!!!"		};	},	// 2.处理输入变化	handleChange:function(event) {		this.setState({			helloTo:event.target.value		});	},	submitHandler:function(event) {		event.preventDefault();		alert(this.state.helloTo);	},	// 3.渲染时value值使用state保存	render:function() {		return (			
); }});ReactDOM.render(
, document.body);

显著的变化就是</input>的值存储在父组件的state中。因为数据流有了清晰的定义。

  1. getInitialState设置defaultValue值。
  2. </input>,其值onChange时,change处理器被调用。
  3. change通过处理函数更新state的值。
  4. 在重新渲染时更新</input>的值。

相比于无约束组件相比,代码量增加了不少,但是现在可以控制数据流,在用户输入数据的时候更新state。譬如想在用户输入的时候将字符都转成大写。

handleChange:function(event) {	this.setState({		helloTo:event.target.value.toUpperCase()	});},

这样我们可以限制可输入的字符集,或者限制用户想邮件地址输入框输入不合法的字符。 你还可以在用户输入数据时,把他们用在其他的组件上。例如:

  • 显示一个有长度限制的输入框还可以输入多少个字符。
  • 显示输入的HEX值所代表的颜色。
  • 显示可自动匹配下拉列表的可选项。
  • 使用输入框的值更新其他UI元素。

###2.3 表单元素的name属性 在React中,name属性对于表单元素来说并没有那么重要,因为约束表单组件已经把值存储到了state中,并且表单提交事件也会被拦截。在获取表单值的时候,name属性并不是必需的。对于非约束组件的表单来说,也可以使用refs来直接访问表单元素。 即便如此,name仍然是表单组件中非常重要的一部分。

  • name属性可以让第三方表单序列化类库在React中正常工作。
  • 对于仍然使用传统提交方式的表单来说,name属性是必需的。
  • 在用户的浏览器中,name被用在自定填写常用信息中,比如用户地址等。
  • 对于非约束单选框组件来说,name是由必要的,他可以作为这些组件分组的依据,确保在同一时刻,同一个表单中用于同样name的单选框只有一个可以被选中。如果不使用name属性,这一行为可以使用约束的单选框实现。

###2.4 多个表单元素与change处理器 在使用约束的表单组件时,没有人愿意重复地为每一个组件编写change处理器,还好有几种方式可以在React中重用一个事件处理器。 示例一:通过.bind传递其他参数。

onChange={this.handleChange.bind(this, 'given_name')}

示例二:使用DOMNode的name属性来判断需要更新哪个组件的状态 组件name="given_name" 提供state的given_name,然后通过如下代码匹配:

handleChange: function(event) {	var newState = {};	newState[event.target.name] = event.target.value;	this.setState(newState);},

示例三:React还在addon中提供了一个mixin,React.addons.LinkedStateMixin通过另一种方式解决同样的问题。

###2.5 自定义表单组件 自定义组件是一种极好方式,可以在项目中复用共有的功能。同时,也不失为一种将交互界面提升为更加复杂的表单组件(比如复选框组件或单选框组件)的好方法。 当编写自定义组件时,接口应当与其他表单组件保持一致。这可以帮助用户理解代码,明白如何使用自定义组件,且无须深入到组件的实现细节里。 我们来创建一个自定义的单选框组件,其接口与React的select组件保持一致。我们不打算实现多选功能,因为单选框组件本来就不支持多选。

var Radio = React.createClass({	// 初始化属性	propTypes:{		onChange: React.PropTypes.func	},	// 初始化state	getInitialState:function() {		return {			value:this.props.defaultValue		};	},	// 事件处理	handleChange:function(event) {		if(this.props.onChange) {			this.props.onChange(event);		}		this.setState({			value:event.target.value		});	},	render:function() {		var children = [];		var value = this.props.value || this.state.value;		React.Children.forEach(this.props.children, function(child, i) {			console.log("children " + child.props.value +" " +child.props.children);			var label = (								);			children[i] = label;		}.bind(this));		return(			
{ children.map(function (name) { return
{name}
}) }
); }});

通过上面的模块,就可以实现任意几个类型为radio的input组件自定义,在父组件中调用代码为:

render:function() {	return (		
);}

在自定义模块render方法的return中,这里处理的不是很好,增加了两个div标签,暂时没想到好的办法,若您有好的办法,可以给我留言。

###2.6 Focus 控制表单组件的focus可以很好地引导用户按照表单逻辑逐步填写,而且还可以减少用户的操作,增强可用性,增强可用性, 因为React的表单并不总是在浏览器加载时被渲染,所以表单的输入域的自动聚焦操作起来有点不一样。React实现了autoFocus属性,因此在组建第一次挂载时,如果没有其他的表单域聚焦时,React就会把焦点放在这个组件对应的表单域中。如下代码:

还有一种方法就是调用DOMNode的focus()方法,手动设置表单域聚焦。

##3 可用性 React虽然可以提供开发者的生产力,但是也有不尽如人意的地方。主要注意以下几点:

  1. 把需求传达清楚,无论对于应用程序的哪部分来说,好的沟通都是非常重要的,对表单来说尤其如此。
  2. 不断地反馈,尽可能快地为用户提供反馈也很重要。
  3. 迅速响应,React拥有非常强大的渲染引擎。他可以非常显著的提升应用的速度。
  4. 符合用户的预期,用户对事物如何工作有自己的预期。
  5. 可访问,可访问性也是开发者和设计师在创建用户界面时容易忽略的一点。
  6. 减少用户的输入,减少用户输入可以大幅提高应用的可用性。

##4 参考

《React 引领未来的用户界面开发框架》

转载于:https://my.oschina.net/feiyangxiaomi/blog/647985

你可能感兴趣的文章
shell语法练习之实现简单计算器
查看>>
windows下时间转换和获取当前时间
查看>>
EM3096二维扫描模块在手持终端设备上的应用
查看>>
HADOOP INSTALL
查看>>
openshift
查看>>
Latex 宏包编写,自定义宏包
查看>>
PHPStorm激活
查看>>
Shiro学习笔记<2>SecurityUtils,SecurityManager,Subject
查看>>
修改数据库密码
查看>>
多态存在的条件
查看>>
使用RestTemplate实现rest服务的调用
查看>>
Linux虚拟化应用之HyperV到KVM的迁移
查看>>
centos7安装docker
查看>>
C++模版函数
查看>>
策略模式
查看>>
我自研主动型氢原子钟将现身空间站
查看>>
maven添加本地jar包
查看>>
PHP 重置数组为连续数字索引的方式
查看>>
致创业者:APP已死 服务永生
查看>>
解决TIME_WAIT过多造成的问题
查看>>