一、前言

组件和复用技术是构建大型应用的必要利器,Vue和Reactjs等框架都很好的支持了组件化开发。本章节重点学习Reactjs的组件化,下面以搜索页面demo介绍:

二、组件封装方法

Vue中组件是通过Vue.component()方法来创建全局组件,对于Reactjs,有如下两种方式:

1、函数模式

这种模式是通过js函数定义一个组件,也是最简单的模式。比如默认demo的App.js中定义的App方法。

function App() {return (<div className="App"><header className="App-header">...</header></div>);
}

该方法返回React元素(一段JSX代码)。

2、类模式

该模式使用ES6的class定义组件,我们将上面的例子改写:

class  App extends React.Component {render(){return (<div className="App"><header className="App-header">...</header></div>);}
};

与函数组件比较,首先是一个继承React.Component的父类,定义了render()方法,return代码段包裹在render()方法中。为了区别W3C默认的组件,Reactjs自定义的组件首字母需要大写。

两者组件模式在React是等效的,我们这里推荐类模式创建,其创建的组件是有的实例,后面会讲到类模式具有一些额外的特性,比如state,生命周期等等。

下面我用就利用类模式创建demo组件。从页面组成看,分三个子组件,搜索栏组件SearchBox,热搜栏组件SearchHot,以及搜索结果组件SearchList,此三个子组件都包含在父组件App中。

新建一个SearchBox.js,创建SearchBox组件并导出。

import React from 'react';class SearchBox extends React.Component{render(){return(<div style={{width:300}}><input type="text"></input>&nbsp;&nbsp;<button>搜索</button></div>)}
}
export default SearchBox

新建一个SearchList.js,创建SearchList组件。

import React from 'react';
import   './SearchList.css';class SearchList extends React.Component{render(){return(<div className="d"><div className="s">搜索结果:</div><ul className="u"></ul></div>       )}};export default SearchList;

新建一个SearchHot.js,创建SearcHot组件。

import React from 'react';
import   './SearchHot.css';class SearchHot extends React.Component{render(){return (<div className="d"><div className="s">大家都在搜:</div><ul className="su"></ul></div> )}
}
export default SearchHot;

App.js代码如下,首先导入SearchList,SearchBox,SearchHot组件,然后类似原生的标签进行调用和渲染。

import React from 'react';
import logo from './logo.svg';
import './App.css';
//导入两个组件
import SearchList from './SearchList.js';
import SearchBox from './SearchBox.js';
import SearchHot from './SearchHot.js'class  App extends React.Component {render(){return (<div className="App">{/* 引入组件 */}<SearchBox /><SearchHot /><SearchList /></div>);}
};export default App;

最终的效果如下:

这里仅仅将demo的框架轮廓画了出来,下面我们将添加相关的数据以及响应。

三、state

state可以看做Reactjs的状态机,保存该组件的相关数据和变量,state的作用域仅在组件内部,可以类比Vue的data。我们在"大家都在搜"模块,利用state构造一组热搜数据,并渲染出来。

1、初始化state

需要注意,只有类模式的组件才支持state,我们首先在SearchHot组件增加构造器函数,并初始化state对象。

constructor(props){// 1、调用父类构造方法super(props);//2、初始化state对象this.state = {hotItem:['口罩','手套','酒精']}
}

该构造方法的入参是props,可以利用该参数进行父子组件的数据传递,下一节我们再详细说明。

1、调用super方法,实现父类构造方法(React.Component)的调用。

2、初始化state对象,为简化流程,我们暂时将写死数据。

下面我们将state的数据渲染到页面区域,定义hostList对象,获取state数据,并拼装li列表(后面章节我们再仔细描述列表,暂时忽略)。

const hotList = this.state.hotItem.map((value)=>(<li key={value.toString()}>{value}</li>)
)

在JSX引入该对象表达式。

return (<div className="d"><div className="s">大家都在搜:</div><ul className="su">{/*引用变量表达式  */}{hotList}</ul></div>
)

完整的代码如下:

import React from 'react';
import   './SearchHot.css';class SearchHot extends React.Component{constructor(props){super(props);this.state = {hotItem:['口罩','手套','酒精']}}render(){const hotList = this.state.hotItem.map((value)=>(<li key={value.toString()}>{value}</li>))return (<div className="d"><div className="s">大家都在搜:</div><ul className="su">{hotList}</ul></div> )}
}
export default SearchHot;

效果如下:

2、更新state

Vue和Reactjs作为MVVM模式,数据驱动DOM的更新和渲染。在Vue中,我们知道通过监听数据的变化,VDOM的diff算法,最终映射到真实DOM的更新。在Reactjs中,其过程是类似的,我们看下如何实现。

在"大家都在搜"中,我们增加"下一批"功能,当点击时,更新hotList数组,页面DOM也及时更新。效果如下:

JSX中添加一个a标签按钮,点击时,调用changeHot方法(关注reactjs的事件,后面章节会介绍)。

<div className="s">大家都在搜:<a href="#" onClick={this.changeHot} style={{marginLeft:100}}>下一批</a></div>

在changeHot方法中更新hotList数据。

changeHot(){this.setState({hotItem:['电视','手机','电脑','平板']})
}

这里要注意,需要调用setState方法更新,不能给state直接赋值,this.state={hotItem:['电视','手机','电脑','平板']},不会生效。这一点和vue不同,vue可以直接对属性进行设置。

此时,大家点击"下一批"按钮,发现报错了。

setState没有定义,这个是什么鬼,因为没有将回调方法绑定this对象,此时changeHot中的'this'是undefined的。有两种方式可以解决。

第一种方法,是在构造器中绑定this

constructor(props){// 1、调用父类构造方法super(props);//2、初始化state对象this.state = {hotItem:['口罩','手套','酒精',]};//绑定thisthis.changeHot = this.changeHot.bind(this);}

第二种方法,将changeHot改造成箭头函数,我们知道箭头函数的this指向函数对象。

changeHot = () =>this.setState({hotItem:['电视','手机','电脑','平板']});

此时,点击"下一批",可以看到页面发生更新。

我们再来做个试验,调用setSate设置hotItem后,立即打印下该值。

changeHot(){this.setState({hotItem:['电视','手机','电脑','平板'] })console.log(this.state.hotItem) }

其结果如下:

并没有更新,why?这是因为setState是个异步操作(这点与vue是不同的,vue对于model的更新是同步的,对于view的更新是异步的,有兴趣的可以了解下vue的nexttick机制),大家可以思考下这样做的目的,如果是同步的,那么每次state更新会立即引起DOM的更新,效率将会非常低;如果是异步的,可以对设置的state进行批量操作,还可以对一些操作进行合并,大大提升效率。事实上vue的view异步更新也是如此,只不过两者实现的机制以及阶段不同而已。

如何要获取到更新后的值呢,setState提供了设置callback方法。

    changeHot(){this.setState({hotItem:['电视','手机','电脑','平板'] },()=>{console.log(this.state.hotItem)})}

这样就能正确的获取到更新后的值了。

完整的代码如下:

import React from 'react';
import   './SearchHot.css';class SearchHot extends React.Component{constructor(props){// 1、调用父类构造方法super(props);//2、初始化state对象this.state = {hotItem:['口罩','手套','酒精',]};//绑定thisthis.changeHot = this.changeHot.bind(this);}changeHot(){this.setState({hotItem:['电视','手机','电脑','平板'] })}//箭头函数//changeHot = () =>this.setState({hotItem:['电视','手机','电脑','平板']});render(){//1、定义变量,使用state对象数据,构造列表const hotList = this.state.hotItem.map((value)=>(<li key={value.toString()}>{value}</li>))return (<div className="d"> <div className="s">大家都在搜:<a href="#" onClick={this.changeHot} style={{marginLeft:100}}>下一批</a></div><ul className="su">{/*引用变量表达式  */}{hotList}</ul></div> )}
}
export default SearchHot;

四、props

上面讲到state是组件内部的状态机,其作用域也对该组件内部。对于组件来说,父子组件的交互,数据传递是基本功能,显然state不具备实现该功能。

我们回顾下Vue的组件间交互流程,父组件通过v-bind绑定待传的属性标签,子组件通过props属性进行接受数据;子组件通过emit方法回调父组件函数,实现子组件向父组件的数据传输。Reactjs的过程也是类似的,我们前一小节在组件构造器中提到了props入参,父组件通过props将数据传递给子组件,子组件回调父组件的方法,实现数据的上传。

下面我们利用搜素结果模块,介绍父子组件数据流的传递,流程的示意图如下:

1、子组件SearchBox负责搜索关键字输入,保存到state中,通过回调父组件App的changeSearchVal方法,将输入值searchVal传递到父组件App。

2、父组件App接受到searchVal后,查询搜索结果,将结果保存到state的searchListVal。

3、父组件App将该值通过props传递到子组件SearchList,子组件SearchList接受到传递的searchListVal后,并渲染出来。

两个子组件SearchBox与SearchList不直接交互,而是通过他们共同的父组件App进行处理和转发。

  • SearchBox组件

(1)input的onChange监听输入值,绑定handleChange方法。

<input type="text" style={{width:150}} onChange={this.handleChange}></input>

handleChange将输入值更新到state中的searchVal(也可以用非受控组件的ref方式获取,后面我们会讲到)

//获取input输入的值,保存到statehandleChange(e){this.setState({searchVal:e.target.value});}

(2)button增加onclick事件,

<button onClick={this.onSearchClick}>搜索</button>

获取state中的searchVal值,并通过回调父组件的changeSearchVal传递给父组件。

 //回调changeSearchVal,将searchVal传递给父组件onSearchClick(){this.props.changeSearchVal(this.state.searchVal);}

我们看到使用了this.props.changeSearchVal,该方法在父组件App中定义,并通过props传递给子组件SearchBox(props不但传递属性值,也可以传递函数)。

SearchBox.js的完整代码:

​
import React from 'react';
class SearchBox extends React.Component{constructor(props){super(props);this.state = {searchVal:""};this.onSearchClick = this.onSearchClick.bind(this);this.handleChange = this.handleChange.bind(this); }//1、获取input输入的值,保存到statehandleChange(e){this.setState({searchVal:e.target.value});}//2、回调changeSearchVal,将searchVal传递给父组件onSearchClick(){this.props.changeSearchVal(this.state.searchVal);}render(){return(<div style={{width:300,margin:10}}><input type="text" style={{width:150}} onChange={this.handleChange}></input>&nbsp;&nbsp;<button onClick={this.onSearchClick}>搜索</button></div>)}
}
export default SearchBox​
  • App组件

App中如何将changeSearchVal方法传递给子组件SearchaBox的呢?App.js中引用SeachBox的时候,增加了changeSearchVal属性(子组件通过this.props.changeSearchVal获取),其值就是该方法的引用。

 <SearchBox changeSearchVal = {this.changeSearchVal}/>

changeSearchVal方法的实现如下;

 changeSearchVal(val){//查询搜索结果,为了简化,写死固定值,模拟查询过程console.log("输入值:"+val);let searchListVal=['牛奶','饼干']//更新state中的searchListValthis.setState({searchListVal:searchListVal});}

在该方法中,入参为输入框的值,为了简单起见,直接写死搜索结果,更新并保存到state的searhListVal中。

下面将搜索结果searchListVal通过props传递给子组件searchList。

 <SearchList searchListVal={this.state.searchListVal}/>

引用SearchList组件,定义SearchListVal属性,并将state的searchListVal赋值给该属性。

App.js的完整代码如下:

import React from 'react';
import logo from './logo.svg';
import './App.css';
//导入两个组件
import SearchList from './SearchList.js';
import SearchBox from './SearchBox.js';
import SearchHot from './SearchHot.js'class  App extends React.Component {constructor(props){super(props);//初始化statethis.state={searchListVal:[]};this.changeSearchVal = this.changeSearchVal.bind(this);}//1、回调方法,changeSearchVal(val){//查询搜索结果,为了简化,写死固定值,模拟查询过程console.log("输入值:"+val);let searchListVal=['牛奶','饼干']//更新state中的searchListValthis.setState({searchListVal:searchListVal});}render(){return (<div className="App">{/* 通过props传递回调方法changeSearchVal */}<SearchBox changeSearchVal = {this.changeSearchVal}/><SearchHot />{/* 通过props传递属性值searchListVal */}<SearchList searchListVal={this.state.searchListVal}/></div>);}
};export default App;
  • SearchList组件

在SearchList组件中,从props中获取searchListVal值,并封装成列表。SearchList.js代码如下:

import React from 'react';
import   './SearchList.css';class SearchList extends React.Component{constructor(props){super(props);}render(){//1、从props中获取searchListVal,封装列表const searchValList = this.props.searchListVal.map((value)=>(<li key={value.toString()}>{value}</li>));return(<div className="d"><div className="s">搜索结果:</div><ul className="u">{/* 引用对象表达式*/}{searchValList}</ul></div>       )}};export default SearchList;

看下最终的渲染效果:

至此,props的基本用法讲完了,但是对比Vue,我们发现有两个问题没有找到答案:

1、父子组件的数据传递是否是单向的,子组件能否更改props的值呢?

2、vue的插槽(slot)在Reactjs中如何实现?

先来看第一个问题,我们在SearchList.js中加入这段代码,改变子组件中的props值。

render(){//修改props的值this.props.searchListVal=['牛奶','饼干'];//1、从props中获取searchListVal,封装列表const searchValList = this.props.searchListVal.map((value)=>(<li key={value.toString()}>{value}</li>));
...
}

结果报错了

Props是只读的,没法修改的,这点与Vue是一致的,父子组件数据传递是单向的,且向下的。

继续看第二个问题,实际上在Reactjs中是没有"插槽",是通过this.props.children的变相实现"插槽"功能

在App.js中增加了一段JSX代码,

<SearchList searchListVal={this.state.searchListVal}><div>为您搜索到2条记录</div>
</SearchList>

将这段代码插入到子组件SearchList中显示。

      return(<div className="d"><div className="s">搜索结果:</div>{/* 引入插槽代码 */}{this.props.children}<ul className="u">{/* 引用对象表达式*/}{searchValList}</ul></div>       )

五、总结

本章节介绍了Reactjs组件的基本知识。

1、创建组件有两种模式,函数模式和类模式,推荐使用类模式。

2、组件内部的状态机state,其作用域仅在其组件内部。

(1)仅在constructor方法中可以使用this.state进行赋值,其他地方统一使用setState方法进行更新。

(2)setState是个异步操作,如需要使用更新后的值,在其callback方法中调用。

3、通过props实现父子组件间的数据传递,其数据流向是单向的,向下的;this.props.children的变相实现"插槽"功能。

玩转Reactjs第三篇-组件(模式stateprops)相关推荐

  1. 游戏设计模式阅读笔记13——解耦模式(组件模式)

    一.意图 允许单一实体跨越多个领域而不会导致这些领域彼此耦合. 二.动机 当一个系统涉及到渲染,物理,声音,控制器输入等等...如果都写在一个类中,这个类就显得巨大且不易维护添加,高度耦合. 在使用并 ...

  2. android 界面组件,安卓开发学习周第三篇——Android中的UI组件

    原标题:安卓开发学习周第三篇--Android中的UI组件 在Android APP中,所有的用户界面元素都是由View和ViewGroup的对象构成的.View是绘制在屏幕上的用户能与之交互的一个对 ...

  3. 玩转Reactjs第一篇-构建工程(create-react-app)

    一.前言 前期对vue的使用以及源码做了一些介绍和分析,vue由于其易用易学,性能高等特点,吸引越来越多的前端开发者在使用.作为前端框架的三剑客之一的Reactjs,得到了facebook社区大力支持 ...

  4. getresourceasstream方法_【设计模式】第三篇:一篇搞定工厂模式【简单工厂、工厂方法模式、抽象工厂模式】...

    文章系列目录(持续更新中): [设计模式]第一篇:概述.耦合.UML.七大原则,详细分析总结(基于Java) [设计模式]第二篇:单例模式的几种实现And反射对其的破坏 一 为什么要用工厂模式 之前讲 ...

  5. 【步兵 经验篇】组件模式的特点

    [步兵 经验篇]组件模式的特点 by EOS. 组件模式对u3d的开发者可能并不陌生,因为其框架设计大量的使用了这种模式, 但是cocos却没有使用,不过后来出的js也开始套用这种模式,他能被效仿,自 ...

  6. 神了!!看完这篇文章我不仅学会了手撸vue三开关组件,还搞懂了父子组件传值

    神了!!看完这篇文章我不仅学会了手撸vue三开关组件,还搞懂了父子组件传值 引子 前置知识 什么是vue组件 父子组件传值 父传子 子传父 model选项的引入 三开关组件(three-switch) ...

  7. AAAI 2020 提前看 | 三篇论文解读问答系统最新研究进展

    机器之心原创 作者:仵冀颖 编辑:H4O 2020 年 2 月 7 日至 12 日,AAAI 2020 将于美国纽约举办.今年 AAAI 共接受了 8800 篇提交论文,其中评审了 7737 篇,接收 ...

  8. NLP能否像人脑一样工作?CMU、MIT三篇论文详解机器和大脑范畴下NLP的关系

    本文转载自公众号机器之心 作为计算机科学领域与人工智能领域的重要研究课题,自然语言处理已经在各领域展开了广泛的研究与探讨.随着研究的深入,一些学者开始探讨机器中的自然语言处理和大脑中的自然语言处理是否 ...

  9. CV圈太卷了!继谷歌提出MLP-Mixer之后,清华、牛津等学者又发表三篇MLP相关论文...

    作者 | 琰琰.陈大鑫 编辑 | 刘冰一 5月4日,谷歌团队在arXiv上提交了一篇论文<MLP-Mixer: An all-MLP Architecture for Vision>,引起 ...

  10. Entity Framework技术系列之2:三种开发模式实现数据访问

    前言 Entity Framework支持Database First.Model First和Code Only三种开发模式,各模式的开发流程大相径庭,开发体验完全不一样.三种开发模式各有优缺点,对 ...

最新文章

  1. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)
  2. 用Eclipse给安卓应用进行签名
  3. mysql sqlserver schema_MySQL数据库数据迁移到SQLserver
  4. 工作222:title写活
  5. 一文读懂目前大热的AutoML与NAS!
  6. 编制一个函数jsValue(),求出千位数上的数减百位数上的数减十位数上的数减个位数上的数大于零的个数cnt,再求出所有满足此条件的四位数的平均值pjz1,以及不满足此条件的四位数的平均值pzj2
  7. Java 多线程 之 wait等待 线程实例
  8. 使用JAVA的keytool生成jks证书,通过jks证书生成pfx证书,tomcat配置https
  9. linux批量分区,Linux磁盘批量分区格式化和挂载脚本
  10. 博客阅读和思考——2018-3-5
  11. 【9933】单词的划分
  12. [译] 为什么需要在 React 类组件中为事件处理程序绑定 this
  13. 解决python中文乱码问题
  14. 成分句法分析综述(第二版)
  15. Linux课程设计报告【全集】
  16. GSM Arena 魅族mx四核评测-个人翻译
  17. 服务器 文件系统,服务器硬盘文件系统
  18. CSS实现a标签去掉下划线以及点击不再有颜色变化
  19. 读取统计微信捐款(matlab)
  20. JWT实战 Spring Security Oauth2整合JWT 整合SSO单点登录

热门文章

  1. 影视后期调色必要性以及操作理论
  2. 计算机大赛获奖作品动画,全国xx杯说课大赛计算机类一等奖作品:《遮罩动画》说课课件...
  3. 我想开发一个小程序,大概需要多少钱?
  4. 我的RHCE认证考试经历
  5. 全球消费精品消博会亮相!科蒂、资生堂、欧莱雅、奔富、保乐力加、红牛、雀巢、拜耳、澳佳宝都来了 | 美通社头条...
  6. c程序怎样往mysql里面添加字符串指针指向的字符串
  7. 深度优先搜索与广度优先搜索
  8. 外部多端口映射Https443端口配置
  9. HTML5 video autoplay=“autoplay“ 无法自动播放的问题
  10. Ghost XP打XP SP3后出现登录不了的问题