R3-React组件从0到1

react

       这一章我们正式从组件开始,通过这一章,基本可以靠自己写出一些基础组件,如展示型的页面,简单表单页面等,文章中所有出现的源码都在GitHub上,共需要的童鞋下载。统一的,**源码链接还是放在了文章的最后面。还有一点如果觉得文章中贴的代码太长影响了阅读可以快速扫一遍,直接阅读文字内容。跑工程源代码的时候再细看。**先来看这一章的思维导图

唉?怎么木有组件的生命周期呢。莫慌!这个是有的,生命周期比较重要,所以打算重点介绍,单独领出来放到下一章了。

1.什么是组件

       你知道飞机✈️的结构吗?机头,机身,机翼和机尾。机头是专门给机长准备的,机身是给乘客的,机翼机尾是用来保持平衡和飞翔的,每个部分都各司其职。组件就好比这些飞机的一部分,飞机就是页面。一个页面往往需要多个组件共同完成,每个组件负责不同的功能,这些组件还能像飞机的各个部分如机翼,不光这个飞机能用,装到其他飞机(同型号)也可以。组件也是这样,具有一定的独立性。有人会问那HTML是什么?他是构成组件的一部分,组件构成不光有HTML还有逻辑js负责,样式css负责。所以我的理解是组件是完成某一特定功能的含有逻辑、样式、结构的独立集合体,且具有一定通用性。

2.组件的创建方式

       第一种:ES6语法格式使用class进行创建,也是目前最流行的方式。通过继承React.Compoent来实现创建组件。里面必须实现render方法。我们之后文章中所有出现的组件都是这种方式创建!

       第二种:通过React.createClass实现,这个已经在react16.0.0版本抛弃了,解决方案是使用create-react-class依赖包。

       第三种:纯函数方式,无状态组件。

三种方式我写在了同一个文件中,需要显示哪一个组件则导出哪一个,代码如下(可以直接运行源码):

import React, { Component } from 'react';

import CreateReactClass from 'create-react-class';

/*

* 组件概念

* 1.组件的创建方式

* 2.无状态组件

* */

// ES6 现在主流创建组件方式

class ComponentConcept extends Component{

constructor(props){

super(props);

}

render(){

const { text } = this.props;

return(

<div>Hello Component</div>

)

}

}

// 16.0.0之前是react官方的创建组件的方式React.createClass,16.0.0之后弃用了

// 如今想在16.0.0及以上版本使用这种方式可使用Create-React-Class包

const ComponentConcept1 = CreateReactClass({

getDefaultProps(){

return {

text:'随便命名',

}

},

render(){

const { text } = this.props;

return (

<div>{text}</div>

)

}

})

// 纯函数,无状态组件,只负责展示,只接收一个props和context作为参数

function ComponentConcept2({text = "hello 纯函数"}) {

return (

<div>{text}</div>

)

}

// export default ComponentConcept;

// export default ComponentConcept1;

export default ComponentConcept2;

render方法,是用来绘制页面的,即你要展示的页面内容都在这里进行编写,有以下几点需要注意
(1).必须有render函数,且必须有return,如果木有return浏览器会报错。
(2).纯函数则相当于render函数,同样必须return。
(3).return返回值必须都包裹在一个标签内。即最外层不可出现两个及以上的同级标签,return后面的小括号不写没关系,但是建议必须写,规范。

return (

<div></div>

<div></div>

)// 错误,包含了两个同级标签。其他返回这种HTML模板的函数都是一样的规则。

这种写法是对的

return (

<div>

<div></div>

</div>

)// 正确,最外层只有一个标签。

3.无状态组件

纯函数概念:

给定相同的输入,她总是返回相同的输出;过程没有副作用;没有额外的状态依赖。

       上面第三个组件就是用纯函数生成,他没有生命周期,也就没有了状态依赖。下一章介绍生命周期,大白话说就是他创建的组件就像一个相框,相框的外形固定,你放什么相片他就展示什么相片,没有自己的更新机制。所以多用来做纯展示型组件。

4.事件系统

       最好先了解一下原生DOM事件,这里有一个传送门DOM事件

       在react开发中,事件是必须的,从最实际的开始,现在我们开发中最常用的方法绑定方式“箭头函数”

 handleBind(e,value){

console.log('handleBind',e,value);

}

// 添加点击事件,仔细体会下面另种方式

<button className='btnStyle' onClick={this.handleBind}>ErrorBind</button>

<button className='btnStyle' onClick={(e)=>this.handleBind(e,'这里可以传值')}>ErrorBind</button>

       上面是一个点击事件,其他事件跟原生DOM事件是一样的,比如onChange,onBlur,onFocus等等,唯一区别就是react事件事件名属于驼峰命名。

       除上述绑定方式外还有另外一种方式

<button className='btnStyle' className='btnStyle' onClick={this.handleBind.bind(this,'ddddddd')}>Bind</button>

<button className='btnStyle' onClick={::this.handleBind}>SimpleBind</button>

       第一行使用.bind,第二行则是第一行的简写形式使用双冒号::进行绑定。

       最后说一下React合成事件的实现原理。

       事件委派,react事件并不是把事件直接绑定到真实的dom上(如果想用原生事件,则必须绑定到真实dom上,这就需要生命周期函数,前期在没有生成真实dom之前原生事件绑定是无效的),而是把事件绑定到了结构的最外层,并使用统一的事件监听器。这个事件监听器上有一个映射,保存了组件中的所有事件监听和处理函数。当触发某一个事件时则会根据映射找到对应的处理函数执行。当组件卸载或挂载时,就会在映射表中删除或添加新的事件监听映射记录。
自动绑定,在react中,每个方法的上下文都是指向该组件的实例,即自动绑定this为当前组件。

       react事件只实现了冒泡,可以像使用原生事件一样使用stopPropagation进行阻止冒泡。这主要是因为捕获阶段很少使用,同时react中是支持使用原生事件的通过ref。下面是整理事件的几种方式,这需要多动手多体会。如果看代码太长可以直接跳读下面的内容,跑源代码的时候再看这些代码!整个工程源码在最下方有链接。

import React, { Component } from 'react';

import '../../style/analysisComponent/analysisStyle.scss';

/*、

* react 事件系统

* 1.事件原理

* 2.绑定方式

* 3.原生事件

* */

class ComponentAndEvent extends Component{

constructor(props){

super(props);

this.state = {

name:"事件",

}

}

// 1.bind

handleBind(e,value){

console.log('handleBind',e,value);

}

// 2.arrow

handleArrow = (e,value) => {

console.log('handleArrow',e,value);

}

// 外层

onClickFather = () => {

console.log(1);

}

// 内层1

onClickSon = () => {

console.log(2);

}

// 内层2

onClickGrandson = (e) => {

console.log(3);

e.stopPropagation();

}

componentDidMount(){

let isCatch = false;

// 是否是在事件分发阶段进行捕获处理,false代表冒泡阶段处理,true代表分发阶段处理

this.refs.grandson.addEventListener('click',e => {

console.log('grandson');

e.stopPropagation();

},isCatch)

this.refs.son.addEventListener('click',e => {

console.log('son');

},isCatch)

this.refs.father.addEventListener('click',e => {

console.log('father');

},isCatch)

}

render(){

return (

<div className='analysisStyle'>

<button className='btnStyle' className='btnStyle' onClick={this.handleBind.bind(this,'ddddddd')}>Bind</button>

<button className='btnStyle' onClick={::this.handleBind}>SimpleBind</button>

<button className='btnStyle' onClick={this.handleBind}>ErrorBind</button>

<button className='btnStyle' onClick={this.handleArrow}>=>方式1</button>

<button className='btnStyle' onClick={(e) => this.handleArrow(e,'ddddddddd')}>=>方式2</button>

<button className='btnStyle' onClick={this.handleArrow()}>Error=></button>

<div className='fatherBox' onClick={this.onClickFather}>

<div className='sonBox' onClick={this.onClickSon}>

<div className='grandsonBox' onClick={this.onClickGrandson}>

冒泡

</div>

</div>

</div>

<div className='fatherBox' ref='father'>

<div className='sonBox' ref='son'>

<div className='grandsonBox' ref='grandson'>

冒泡

</div>

</div>

</div>

</div>

)

}

}

export default ComponentAndEvent;

5.props

       流向:react数据流是自上而下的单向数据流。媒介:props属性

       props是在react中编写组件时常用的属性之一,数据的流动靠的就是它。是组件之间相互联系的一种机制!我们所创建的每一组件都会有一个自带的props属性。它主要承载了来自父级的数据,比如

// 子组件Son的render函数

render(){

const { name } = this.state;

const { hello, whoUse } = this.props;

return (

<div>

<div>{`姓名state: ${name}`}</div>

<div>我是Son</div>

<div>{whoUse}</div>

<div>{hello}</div>

</div>

)

}

       这是一个子组件(子组件名字叫Son)的render函数,我们从this.props中取出两个属性值,hello,whoUse两个。这两个值从哪里来的?注意这两个值就是调用该组件的时候在组件中传入的。开下面的调用处的代码:

// 父组件Father的render函数

render(){

const { name, isShowChild, isAdd } = this.state;

console.log('father',this.props);

return (

<div>

<div>{`姓名state: ${name}`}</div>

<div>下面是一个子组件:</div>

<Son whoUse={'father component'} hello={'hello word'}>

你好我是夹在子组件标签中的内容,显示了吗?

</Son>

</div>

)

}

结果

       最后两行显示的值正式在父组件中调用Son这个组件时传入了whoUse和hello这两个属性值。如果不传则在子组件中拿不到该值。

       在仔细看发现“你好我是夹在子组件标签中的内容,显示了吗?”这句话没有显示。**实际上,除了刚才说的以属性方式放在组件中的值会被放置到props上,其标签内部的所有内容都则会被放进children中即this.props.children。**所以当子组件标签中还包含了内容时,可以在子组件的render函数中跟取出标签属性一样,取出children。如下修改:

// 子组件Son修改后的render函数

render(){

const { name } = this.state;

const { hello, whoUse, children } = this.props;

return (

<div>

<div>{`姓名state: ${name}`}</div>

<div>我是Son</div>

{children/*显示子组件标签内部的嵌套内容*/}

<div>{whoUse}</div>

<div>{hello}</div>

</div>

)

}

       所有从父组件中传入的值都会绑定到props这个属性上,可以是任意值,布尔,数字,对象数组等等。

6.state

       props是承载力父级组件传入的数据,组件本身的数据(需要展示带有状态的数据)应该放在哪?当然是state来解决。

     (1)初始化state,建议在constructor函数中初始化。

constructor(props){

super(props);

this.state = {

name:"Father Component",

isShowChild: true,

isAdd:false,

}

}

       也可以在class类内部创建,不需要this关键字,在其他方法中使用时使用this.state.

class Father extends Component{

constructor(props){

super(props);

}

state = {

name:"Father Component",

isShowChild: true,

isAdd:false,

}

...// 省略下面代码

     (2) state是一个对象,state中任何一个数据发生变化都会触发render函数刷新页面。所以需要刷新的页面数据可以定义在state中。

     (3)更新state中的值必须使用setState方法,this.state.xxx = xxx;这种是不行的。setState方法接收两个传参数,第一个是对象,即用来更新stage中属性值得,第二个是匿名函数,是setState更新完值之后调用的(因为setState是异步方法)。

       看下面的代码(组件Son代码片段,工程源码在文章最后有GitHub链接,可下载):

// 组件Son代码片段,运行源码看打印值。

changeBaseNum = () => {

this.baseNum2++;

this.setState({result:this.baseNum2*this.baseNum1},()=>{

console.log(2,this.state.result);// result更新之后调用,打印值是更新后的值

});// 会触发刷新

// this.state.result = this.baseNum2*this.baseNum1;// 错误,不会触发刷新

console.log(1,this.state.result);// setState方法是异步的,会先打印这个,且result值为改变。

}

7.受控组件和非受控组件

       大白话讲就是受到控制的组件和不受到控制的组件,受到谁的控制,当然是state了。受控和非受控主要指表单是否受控。

       1.如果含有表单的组件是受控的,即表单的值变化是根据state变化的,state也是跟着表单值变化的;

       2.非受控则是state可以跟着表单值变化,但是表单值并不会跟着state变化,即不受state控制。

       我们写两个含有表单的组件,一个是受控的,一个是非受控的。

       如何体现受控和非受控:

       1.表单都含有两个输入值姓名和地址;

       2.state中都各自定义好nameValue和addressValue两个属性值;

       3.并给两个input都添加onChange事件。

       4.addressValue变化时,我们都在onChange事件中设置值为“我任性,地址就是泰坦星”,观察受控和非受控页面显示情况;

       不同点:

       1.我们在render函数中受控组件nameValue和addressValue是给了value,非受控组件给了defaultValue。

具体代码如下:

/*

* 受控组件

* */

class ComponentControl extends Component{

constructor(props){

super(props);

this.state = {

componentName:'ComponentControl',

nameValue:"",

addressValue:"",

}

}

handleNameChange = (event) => {

this.setState({nameValue:event.target.value});// 获取输入值并更新到state的nameValue值上

// 受控组件,注释掉上面一行则页面上输入框无法输入,因为输入框value值受state控制,state初始值是空,无变化,则页面显示一直是空。

}

handleAddressChange = (event) => {

this.setState({addressValue:"我任性,地址就是泰坦星"});

}

render(){

const { nameValue, addressValue } = this.state;

return(

<div className='analysisStyle'>

<h2>受控组件</h2>

<input className='inputStyle' type='text' placeholder='输入姓名' value={nameValue} onChange={this.handleNameChange}/>

<br/>

<input className='inputStyle' type='text' placeholder='输入地址' value={addressValue} onChange={this.handleAddressChange}/>

<br/>

<div>姓名:{nameValue}</div>

<div>地址:{addressValue}</div>

{/*调用一下非受控组件,显示在一起便于观察*/}

<ComponentUncontrol />

</div>

)

}

}

非受控组件

class ComponentUncontrol extends Component {

constructor(props){

super(props);

this.state = {

componentName: 'ComponentUncontrol',

nameValue:"",

addressValue:"",

}

}

handleNameChange = (event) => {

// 非受控组件同样对state的nameValue值进行更新,看input输入框显示和下面显示是不一样,因为input输入框的值不受控state控制

this.setState({nameValue:'你好,我是谁?'},() => {

console.log(this.state.nameValue);

});

}

handleAddressChange = (event) => {

this.setState({addressValue:"我任性,地址就是泰坦星"});

}

render(){

const { nameValue, addressValue } = this.state;

return(

<div className='analysisStyle'>

<h2>非受控组件</h2>

<input className='inputStyle' type='text' ref='nameValue' onChange={this.handleNameChange} defaultValue={nameValue} placeholder='输入姓名' />

<input className='inputStyle' type='text' ref='addressValue' onChange={this.handleAddressChange} defaultValue={addressValue} placeholder='输入地址' />

<div>姓名:{nameValue}</div>

<div>地址:{addressValue}</div>

</div>

)

}

}

然后我们对这两个值都实时在页面上输出。

结语:通过这一章,你一已经可以写一些简单的组件了。知道什么是组件,如何创建组件,如何给事件添加事件监听,react合成事件的原理,如何刷新页面,知道什么是受控组件和非受控组件。下一章介绍【组件的生命周期】

工程源码地址,点击这里

以上是 R3-React组件从0到1 的全部内容, 来源链接: utcz.com/z/381446.html

回到顶部