react getelementbyid中getDefaultProps和getInitialState的区别是什么

React.js源码学习,高大上的玩意~【中国计量学院吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:87,079贴子:
React.js源码学习,高大上的玩意~收藏
之前发布的博客系统教程,目前已经完工了,现在开始新的一节,这是关于目前前端业界最流行的一款框架,React.js,它是个什么东西!它是世界著名互联网公司Facebook的技术产品,它存在的目的无非就是将大规模的前端工程变得更加容易开发和维护管理,它的几大特性,比如强调组件化,使用组件的方式采用未来web Components规范中的声明式使用方式,聚焦于视图层,单向数据流模式使得UI组件状态的维护管理更加清晰,借助jsx来写高内聚UI组件,虚拟DOM使得渲染性能更加强劲!下面直接进入正题,需要先入门的同学可以先移步React中文网(Reactjs*cn,*换为.)对于React.js源码分析,必然是需要技巧的,因为react代码有超过2w行的代码,这个如果是阅读已经构建好的单文件代码的话,那将会是非常痛苦的,所以我clone了它的代码,然后找到build/modules目录首先找到React.js,这个文件相当于是整个框架的入口,因为modules目录下的所有js文件都是遵循commonjs模块规范来依赖模块的,这样就比较容易的看清楚模块之间的关系。对于入口模块React.js它给用户暴露了一些顶层API,下面给大家详细介绍一下这些API,后面会有关于这些API源码级别的分析1.Children对象,该对象里有几个方法map,forEach,count,only这几个方法主要用于操作React组件的子节点map用于遍历和修改子节点,forEach仅仅只是遍历子节点,count是求子节点个数比如var NotesList = React.createClass({
render: function() {
{//使用{}包围是jsx中的js表达式语法,表达式是需要返回值的,可以返回一个字符串,也可以返回html结构
React.Children.map(this.props.children,function(child){//给map传一个chlidren对象和一个回调来遍历子节点,同时将子节点替换为li标签所包围的新节点,原来是span标签
return &li&{child}&/li&
}});React.render(
&NotesList&
&span&hello&/span&
&span&world&/span&
&/NotesList&,
document.body);2:Component类,这玩意就有点高大上了,迎接EcmaScript6的特性,因为ES6支持类的定义了,所以写React组件将只需要ponent就行了比如class HelloMessage ponent {
render() {
return &div&Hello {this.props.name}&/div&;
}}React.render(&HelloMessage name=&Sebastian& /&, mountNode);原来老旧二逼式用法就是像上面那个例子一样,用的createClass,所以建议初学者果断用es6语法吧,还有,不要担心浏览器不支持,有babel这个工具可以把es6转换成es5甚至es3(注:es4是flash中的action script语法)3:DOM对象,它就是React的虚拟DOM了,你使用jsx就不用关心它,如果你不想使用jsx这种看似丑陋的语法,你就必须得用它了,比如创建一个DOM节点,你在jsx里可以和写html一样&div&hello world&/div&但是,你不用jsx,React.DOM.div(null/*这是节点属性的定义*/, 'Hello World!'),哈哈,够累的吧4:PropTypes对象,该对象中有很多数据类型验证器,用于限制传递给某个Component的数据的数据类型,这样做的效果显而易见,为了组件的健壮性!后面会讲这些验证器的设计实现的!5:createClass方法,这玩意就是一个生产React组件的工厂方法。仅此而已,不过它也是React的核心中的核心6:createElement方法,它其实和3中的DOM使用场景一样,对于不用jsx的时候需要使用,不过前者只能创建普通的DOM节点,后者可以创建React Component节点,所以你想一下,对于混合使用DOM和React Component的场景,还是使用它比较合适的,下面是它简单的用例var CommentBox = React.createClass({
displayName: 'CommentBox',
render: function() {
React.createElement(&div&, {className: &commentBox&},
React.createElement(&h1&, null, &Comments&),
React.createElement(CommentList, null),
React.createElement(CommentForm, null)
}});7:cloneElement方法,复制节点!8:createFactory方法,根据指定的类型创建一个节点工厂,它返回的是一个函数,它就是createElement的包装层,利用它可以少写代码!后面分析源码的时候会详细说明!好了,今天就写到这里吧,明天再更,好累
今天继续讲。9:cerateMixin方法,我所研究的v0.14.0还是测试版,所以还没实现,不过注释里写了用于验证mixin和输出mixin10:constructAndRenderComponent 方法,也是一个便利方法,它包含了React节点的创建过程和渲染过程React.constructAndRenderComponent(React.createClass(....),null,container) createClass创建了一个组件类型,null是传递给组件的属性值,container是渲染容器11:constructAndRenderComponentByID方法和10一样,也是一个便利方法,只需要给它传歌容器的id就行了12:findDOMNode方法是将已经被挂载到浏览器DOM树上的React虚拟DOM节点从浏览器DOM树中找出来,返回的是原生的浏览器DOM节点对象13:render就是核心方法了,渲染方法,它接受2个参数,第一个是ReactElement,相当于是React的DOM节点树,第二个是容器对象,它必须是原生浏览器DOM对象,这样才能将React DOM树挂载到上面去,利用jsx语法可以不需要自己手动建树,直接在第一个参数位置使用html语法就行了14:renderToString这玩意有意思了,用于服务器端渲染React节点树,然后返回html字符串,这样做的效果显而易见为了不让前端花销太大,服务器端直接把渲染结果搞定了之后,前端的React可以直接分析这个已经建立好的节点树,因为它渲染出来的html结构中附带了一些静态数据,用于帮助前端React建立上下文的静态数据,这样就能无缝平滑的使用服务器端渲染出来的组件,这就是传说中的同构应用了15:renderToStaticMarkup 这玩意和14差不多,不过它不会附带数据,所以前端就不能从它渲染出来的html结构来解析出React组件咯.16:unmountComponentAtNode方法是销毁指定容器内的所有React节点17:isValidElement判断是否是一个合法的React节点元素ok,主文件已经讲解完成,后面我打算按照模块树的顺序来讲,比如将a.js,但是它依赖了,b.js,c.js那我会同时讲这3个模块,一颗树一颗树的讲解,是比较符合人类思维的。
你为什么不发react.js吧,而要发到学校的贴吧
虽然一句话也没看懂
ok,继续讲,对于React源码的阅读,我就先从React为前缀的模块文件,比如ReactClass,ReactElement,这些都是核心模块,好,那今天就讲,ReactClass这个模块大概有1k行的代码,可以说是非常多了模块分为几个部分:1.依赖:依赖了ReactComponent,ReactElement等等这些React带头的核心模块,同时也依赖了一些工具模块2.方法描述策略(SpecPolicy):这里通过一个keymirror创建了一个方法描述的键镜像,这个键镜像是一个枚举结构,它当然也是一个对象,但它的键名和其对应的键值相等,为什么要键名和键值相等是有原因滴!下面给个例子:var mirror1 = {a:0,b:1,c:5}if(mirror1.a == target){
.........}var mirror2 = keyMirror({a:null,b:null,c:null})if(mirror2[target] == mirror2.a){
........}这两种通过枚举来验证明显第二种比较安全,第一种target如果等于false它也会执行下面的程序,等于undefined,null都会执行下面的程序,而后者,只有target等于&a&的时候才会执行下面的程序,最重要的是,第二种方法能让你很清楚这个分支条件,就是起到了非常好的描述作用!总之,这就是一种策略,童鞋们写程序可不要再像码农一样的傻乎乎的写代码了哦,要像一个工程师一样,借助各种策略方法来优化问题3:injectedMixins这是一个数组,用于保存被混入的对象4:React类接口(ReactClassInterface),利用了之前keymirror创建出来的键镜像来作为这些接口key的值,用于描述方法的特性,2中有说,这是一种策略,策略为的只是更加高效的执行工程化开发。下面说一下这些接口,mixins 混入对象组statics 静态属性或方法propTypes 组件暴露属性数据类型验证器contextTypes 上下文属性数据类型验证器childContextTypes 子级上下文属性数据类型验证器getDefaultProps 初始化组件属性方法,返回一个对象作为props的初始值getInitialState 初始化组件状态方法,返回一个对象作为state的初始值getChildContext 初始化组件子级上下文方法,一般用于父组件给子组件传值的方法render 渲染组件方法componentWillMount 在组件挂载到DOM之前调用componentDidMount 在组件挂载到DOM之后调用componentWillReceiveProps 组件属性在接受新值之前调用shouldComponentUpdate 作为拦截器,在组件状态或者属性更新之前调用,如果返回false则阻止更新componentWillUpdate 在状态和属性刚接受的时候调用,这个方法在渲染初始化的时候不会被调用的,还有该方法不能用this.setState来设置状态,因为如果允许设置setState那就产生无限递归咯。。。componentDidUpdate 在组件的状态更新且同步到DOM之后调用,这个方法在渲染初始化的时候也是不会被调用的,所以要初始化还是选用之前的接口吧,componentWillUnmount 在组件被卸载之前调用updateComponent 更新组件状态,属于先进的方法,开发者通过重写updateComponent来实现自己的差异化算法,非常不错的啊,达到了可定制化的效果这些接口就是用于组装一个组件的核心接口了,注意这里使用了通过传递回调来控制整个组件的生命周期中的各种动作,从初始化一直到组件销毁都能很好的控制。好了,今天就说这些吧,未完待续。。。。
下面继续讲ReactClass,争取在今天讲完,这个模块可以说是源码阅读者的第一道大山,征服它吧!5:RESERVED_SPEC_KEYS(保留的规范key),它是一个对象,其中有几个方法displayName方法给组件的构造器定义一个标志名,这是非常有用的,因为后面的差异化算法涉及到利用该标志名mixins方法,将传入的mixin数组混合到组件构造器中,其实就是将每个mixin对象(每个mixin对象其实也是一个协议规范,就是4中提到的接口协议)都合并到构造器的prototype中去,它是通过调用私有工具函数mixSpecIntoComponent来实现合并的,这里涉及执行过程中的一些字段验证,比如validateMethodOverride验证是否是可覆盖的方法,判断是否存在嵌套mixin,然后递归合并,注意它在合并的时候是选择非原型属性方法来合并的,其实就是用hasOwnProperty来过滤的。然后这里有一个非常牛逼的设计模式,通过判断协议规范中的接口名称是否存在于RESERVED_SPEC_KEYS中来自动化执行对应的操作,比如RESERVED_SPEC_KEYS = {
hello:function(name){
console.log(&hello &+name);
}}if(RESERVED_SPEC_KEYS.hasOwnProperty(prop)){
RESERVED_SPEC_KEYS[prop](value)}然后你传入的接口规范仅仅只是需要传:{
hello:&janry&}它就自动打印出hello janry了,对于不是保留接口的情况则继续验证,验证是否是ReactClassInterface中所定义的字段,验证是否在它的原型链中已经定义,验证该字段是否是一个函数,判断一个函数是否需要自动绑定,对于不属于ReactClassInterface,而且还是个函数,同时在原型链中还未被定义的则执行自动绑定上下文。它的自动绑定机制是将该方法名称添加到构造器prototype中的__reactAutoBindMap(自动绑定映射表)中,然后通过一个批量绑定方法来绑定上下文,对于不需要自动绑定,比如ReactClassInterface中预留的接口方法,则根据之前说的键镜像对象来执行各种分支动作,我晕,貌似贴子是不能编辑的,上个帖子忘记列举出键镜像对象了,现在说一说DEFINE_ONCE 这个枚举类型表示只能在当前组件类接口规范或者mixins中定义一次的方法,不能在两者之中都定义DEFINE_MANY 这个枚举类型表示在当前组件类接口规范或者mixins中都可以定义的方法,注意,这里会生成一个调用两次该方法的函数,第一次是调用类规范中的,第二次是调用mixins中的OVERRIDE_BASE 这个枚举类型表示必须通过覆盖才能实现的方法DEFINE_MANY_MERGED 这个枚举类型表示当前组件类接口规范或者mixins中都可以定义,注意,这里会生成一个合并两者结果的函数,然后挂载到当前组件原型链中去,方法名不变,如果返回结果中出现key冲突就会抛错。如果你认真看了代码之后,你可能会发现为毛ReactClassInterface中定义DEFINE_MANY的可以是对象也可以是方法,但后面直接就调用该属性而不会再验证它是对象还是方法了,不过你再仔细看看之后就会发现,在mixSpecIntoComponent合并前就验证RESERVED_SPEC_KEYS是否存在该属性,而且恰好RESERVED_SPEC_KEYS中key所对应的值在ReactClassInterface都是非函数类型的,RESERVED_SPEC_KEYS实现了将非函数类型的对象指令转换成了可执行的函数,就这样,对于验证到DEFINE_MANY的也都只是函数了(注意:对于DEFINE_ONCE的方法,如果原型链上已经定义了将不作任何操作,如何未定义,就直接挂载上去就行了),所以直接调用,ok,到这里也把mixSpecIntoComponent函数的实现讲完了,注意调用mixSpecIntoComponent是在ReactClass.createClass中调用的,这是它的核心方法,然后经过这一道工序,基本上把组件的构造器的原型链组装好了,然后讲下构造器的构造函数!它主要有以下几个流程:执行自动绑定开发者自定义方法的上下文然后初始化组件的props,state,contextok,下面再来捋一捋。对于ReactClass中的createClass方法它主要有以下几个流程》首先createClass先接受一个spec规范对象》然后内部定义一个组件构造器Construct,接受两个参数一个是props,一个是context》然后构造器的构造函数中将开发者自定义的方法给自动绑定了上下文到当前组件上》然后根据参数初始化props,context,state的初始化是根据组装后的原型链方法来初始化的》实例化一个ReactClassComponent对象初始化构造器的原型链》合并mixins中的方法或属性到构造器原型链上或者挂载到构造器上》然后根据spec规范对象组装构造器原型链上的相关方法和属性,也包括组装构造器的静态属性或静态方法》完工!
那微博呢,地址看看咯
Ealion Soon&不可能再回头了.....
的确 , 有人分享一点技术知识 ,指不定就能帮当想学的同学 ~但是 推荐发这个的时候小尾巴跟个自己博客的地址 毕竟使用markdwon编辑过的代码文看起来舒服的多...
楼主 加个QQ好友~
有格式好一点的地方吗。在这里看着太难受
登录百度帐号推荐应用React Native之入门,一种颠覆App格局的技术!
React Native之入门,一种颠覆App格局的技术!
水滴恋之风景
背景随着各种技术的发展,目前市场有各种形式的App,当前主要如下几类:. Web App(SUI,MUI,ionic等框架).Hybrid App (新闻类等).Native App (展业家).React Native (竞品:Weex等)优缺点:/pd/123646.htmlreact简介React Native 于日由facebook发布,发布首周在github(/facebook/react-native )就超过了5000星,主要使命是为父出征,与 Apple 和 Google抗衡,为开发者带去一套跨平台、动态更新的Java框架,主要使用js和React开发跨平台移动应用。口号是:Learn once, write anywhere。在试图推翻 Android 和 iOS 压制的同时,还提携了一把自家兄弟:React。RN优点:1.跨平台,同时兼容安卓和iOS2.热修复(iOS上已被苹果干掉)3.开发效率高,维护成本低4.无限接近原生的体验RN缺点:1.新事物,坑多,不够完善2.listview不能复用,效率低3.iOS7以上,安卓4.1以上环境搭建一.准备工作1.1 HomebrewHomebrew, Mac系统的包管理器,用于安装NodeJS和一些其他必需的工具软件。/usr/bin/ruby -e &$(curl -fsSL /Homebrew/install/master/install)&译注:在Max OS X 10.11(El Capitan)版本中,homebrew在安装软件时可能会碰到/usr/local目录不可写的权限问题。可以使用下面的命令修复:sudo chown -R `whoami` /usr/local1.2 Node使用Homebrew来安装Node.js.React Native需要NodeJS 4.0或更高版本。brew install node安装完node后建议设置npm镜像以加速后面的过程(或使用科学上网工具)。npm config set registry https://registry.npm.taobao.org --globalnpm config set disturl https://npm.taobao.org/dist --global1.3 WatchMan(选装)该插件用于监控bug文件和文件变化,并且可以触发指定操作brew install watchman1.4 Flow(选装)flow是一个JS的静态类型检查器,以方便找出代码中可能存在的类型错误brew install flow二.React Native安装2.1 安装RNnpm install -g react-native-cli创建应用1、创建一个工作空间,然后打开终端进入到该目录cd /Users/{user}/Documents/ReactNative/2、运行React Native的初始化命令,创建一个工程HelloWorldreact-native init HelloWorld3、运行工程先进入到我们创建工程目录里面cd /Users/{user}/Documents/ReactNative/HelloWorld之后运行程序,第一次运行会比较慢,他会下载运行所依赖的库react-native run-android 或 react-native run-ios生命周期getDefaultProps:在组件创建之前,会先调用getDefaultProps(),这是全局调用一次,严格地来说,这不是组件的生命周期的一部分。在组件被创建并加载候,首先调用getInitialState(),来初始化组件的状态。getInitalState:组件示例创建的时候调用的第一个函数。主要用于初始化state。注意:为了在使用中不出现空值,建议初始化state的时候尽可能给每一个可能用到的值都赋一个初始值。componentWillMount:在render前,getInitalState之后调用。仅调用一次,可以用于改变state操作。render:组件渲染函数,会返回一个Virtual DOM,只允许返回一个最外层容器组件。render函数尽量保持纯净,只渲染组件,不修改状态,不执行副操作(比如计时器)。componentDidMount:在render渲染之后,React会根据Virtual DOM来生成真实DOM,生成完毕后会调用该函数。在浏览器端(React),我们可以通过this.getDOMNode()来拿到相应的DOM节点。然而我们在RN中并用不到,在RN中主要在该函数中执行网络请求,定时器开启等相关操作引用第三方插件1.cd 到项目根目录2.npm i react-native-tab-navigator --save (注:react-native-tab-navigator为插件名字)
本文仅代表作者观点,不代表百度立场。系作者授权百家号发表,未经许可不得转载。
水滴恋之风景
百家号 最近更新:
简介: 让你的网络生活更丰富多彩!
作者最新文章React使用小结_Javascript教程-织梦者
当前位置:&>&&>& > React使用小结
React使用小结
本文将为关注织梦者的朋友提供的是的React使用小结相关教程,具体实例代码请看下文:
园子都荒废两个月了,实在是懒呀..
近段时间用React开发了几个页面,在使用过程中着实碰到了一些问题,估计刚开始学习的伙伴们都会遇到各种各样的坑
总结记录一下,只看文档是碰不上问题的,内容基础也不基础,高手还请绕道哈哈哈
文章略长,整个目录吧,想看哪儿看哪儿
 基本使用
同一页面中使用
独立文件中使用
return后面只能有一个父级
{}中嵌套JS表达式
受限的HTML属性
智能的...展开操作符
事件绑定与event对象传值
需闭合标签
  属性、状态
  组件的三种定义方式
函数式定义
React.createClass方式定义
ponent方式定义
  组件的生命周期
实例化期(Mounting)
存在期(Updating)
销毁期(Unmounting)
  组件间的通信
  受控组件与非受控组件
非受控组件
  组件的复制
弹窗中的组件并不是在弹窗之后才加载,其实是初始就加载
一、基本使用
1. 同一页面中使用
首先,需要核心库react.js与React的DOM操作组件react-dom.js
其次,如果需要在当前HTML页面中直接写react的代码,就要引入browser.js文件,用于解析相关的JSX语法,同时,script标签指定好type
引入browser是为了在端能直接解析JSX,不过相当耗时,所以建议在本地解析之后再引入ES5的语法文件。当然,JSX语法是不必要的,只是推荐使用。
通过ReaactDOM.render方法渲染,参数1指定组件,参数2指定标签元素
2. 独立文件中使用
使用babel工具对文件进行解析,Sublime Text中怎么配置babel编译?
查看编译后的文件
可以看到,JSX语法,核心就是React的createElement方法,我可以也直接使用这个方法创建。
这一丁点代码就编译了那么久,确实应该在本地先编译好
除了直接在浏览器引入react和react-dom之外,既然需要本地先编译,也可以使用构建工具如Webpack,不仅支持ES6与JSX的解析,还提供了一系列如代码压缩文件合并的功能,也便于管理,不必每次都得手动编译
可以通过npm工具安装react和react-dom包后,引入直接使用(需要ES6基础)
这里就不展开说明了,有兴趣的可以自行去查查相关用法
JSX是React中和重要的部分,直观的表现是将HTML嵌入到了JS中,通过工具(如Babel)编译成支持的JS文件
var Info = React.createClass({
render: function() {
return &p className="user"&{this.props.name}&/p&
ReactDOM.render(
&Info name="Jack" /&,
document.getElementById('box')
可以看到,return关键字后接上一个&p&标签,其中使用{}置入了JS语法。
1. 需要注意的是,return后面只能有一个父级标签
var Info = React.createClass({
render: function() {
return &p className="user"&
this.props.name == 'Jack' ?
&span&is Jack&/span&
2. {}中可以嵌入JS表达式,常见的是三目运算符与map操作
需要注意的是,三目运算符之后也只能接一个父级的标签,否则会报错
还可以置入组件
var Jack = React.createClass({
render: function() {
return &p&I'm Jack&/p&
var Pual = React.createClass({
render: function() {
return &p&I'm Pual&/p&
var Info = React.createClass({
render: function() {
&div className="user"&
this.props.name == 'Jack' ?
ReactDOM.render(
&Info name="Pual" /&,
document.getElementById('box')
3. 在JSX中,HTML的属性是受限的
在HTML标签中使用非原始HTML支持的属性(可加前缀data-),会被React忽略,class关键字需要换成className等
事件绑定需要使用camelCase形式(如onClick)
var Info = React.createClass({
render: function() {
return &p className="user" me="me" name="myName"&{this.props.name}&/p&
4. 智能的...展开操作符
JSX支持ES6中很多语法,包括...这个东西。有时不想一个个属性写,为了代码美观,可以使用
var Info = React.createClass({
render: function() {
var myAttr = {
'title': 'myTitle',
'age': 10,
'data-age': 10,
'onClick': function() {
console.log('onClick');
'onclick': function() {
console.log('onclick');
return &p className="user" me="me" {...myAttr}&{this.props.name}&/p&
ReactDOM.render(
&Info name="Jack" /&,
document.getElementById('box')
编译后将自动展开,其中age被忽略,data-age被保留,onclick被忽略,onClick被保留
5. 事件的绑定与event对象传值
由于React对事件的绑定处理忽略了原始支持的onclick属性,在使用其他JS库时,可能会遇到问题
如WdatePicker日期插件,它的使用方式是直接在HTML中绑定
&input type="text" name="" onclick="WdatePicker()" /&
&input type="text" name="" onClick="WdatePicker()" /&
但转到React中就不适用了,onclick会直接被忽略,onClick因为传的不是函数也被忽略,所以需要换个法子
render() {
// return &input type="text" name="" onclick="WdatePicker()" /&
// return &input type="text" name="" onClick="WdatePicker()" /&
let clickEvent = {
onClick: function(e) {
console.log(e);
WdatePicker(e);
return &input type="text" name="date" ref="date" {...clickEvent} /&
这样一来就能绑定上事件,此日期插件需要一个event对象,然而点击后报错了,调试输出该对象似乎有些奇特
再换种方式,在组件渲染之后直接绑定,成功
componentDidMount() {
let date = ReactDOM.findDOMNode(this.refs.date);
date.onclick = function(e) {
console.log(e);
WdatePicker(e);
虽说这是插件使用方式的不合理,但React传过来的event对象也已经不是原始的event对象了
6. 支持自闭合的标签,要显示地给它关闭
举个例子,对于&input&标签
&input type="text" &
一般的HTML中这样是支持的,但在JSX中会报错
需要加个斜杠,同理用于&img&等标签
&input type="text" /&
三、属性、状态
React中有属性与状态之分,都是为了方便存储或管理数据
1. 属性(props)
一旦定义,就不再改变的数据
一般来说,会通过在HTML标签中添加属性的方式,让子组件获取到该props
ReactDOM.render(
&Info name="Jack" /&,
document.getElementById('box')
则Info组件中就可以通过this.props.name获取到该属性
也可以在组件中自己定义初始的属性,如果父有传name属性,则该初始属性被覆盖
getDefaultProps: function() {
name: 'defaultName'
还可以定义属性的类型,是否必须
propTypes: {
name: React.PropTypes.string.isRequired
这里定义了name属性必须有且为字符串,假设传入的是number类型(注意使用{}包裹,否则始终是字符串),则有警告
ReactDOM.render(
&Info name={10} /&,
document.getElementById('box')
虽然有修改props的方法,但不建议对props进行修改,如果要修改,就使用state吧
2. 状态(state)
状态是React中定义之后可改变的数据,只能在组件内部定义
getInitialState: function() {
在需要修改状态的时候,调用this.setState()方法即可(注意不能直接设置this.state = newObj)
this.setState({
age: this.state.age + 1
注意必须初始化state对象,即初始化时至少要返回一个空的state对象,age属性的初始化是不必要的,只是为了便于管理
React的setState方法是异步的,在其中取state.age可能取不到预期的值(不过目前还没遇到过)
这里的异步包含了两个概念
2.1 调用的时机异步
React的组件有生命周期,在componentWillUpdate与render这两个时期之间才会调用
2.2 调用之后的异步
setState实际上是一个异步方法,可带两个参数
    this.setState({
age: this.state.age + 1
}, function() {
更好的做法是直接在第一个参数使用函数,如此便保证了函数内部能取到正确的值,在大型复杂的组件中推荐如此
this.setState(function(prevState, props) {
age: prevState.age + 1
四、组件的三种定义方式
React推荐将大部件划分为一个个小部件,解耦。而组件的定义,常见的有三种方式
1. 函数式定义
使用函数的方式定义,它的特点是无状态,实际上它并没有被实例化,所以无法访问this对象,不能管理生命周期
多用于纯展示的组件
function Info(props) {
return &p&{props.name}&/p&
ReactDOM.render(&Info name="Jack" /&, document.getElementById('box'));
函数组件接受一个属性参数,可直接获取
2. React.createClass方式定义
这种方式看起来像是ES5的形式,较普遍,根据官方说明,将被类形式取代
var Info = React.createClass({
getInitialState: function() {
name: 'myName'
render: function() {
return &p&{this.state.name}&/p&
在其中也可以使用ES6的语法,为了和类形式的做些区别,代码多写了点
let Info = React.createClass({
getInitialState() {
name: this.props.name || 'myName'
getDefaultProps() {
year: new Date().getFullYear()
showYear(e) {
console.log(this);
let elem = ReactDOM.findDOMNode(e.target);
console.log('year ' + elem.getAttribute('data-year'));
render() {
return &p onClick={this.showYear} data-year={this.props.year}&{this.state.name}&/p&
绑定了点击事件,在点击函数处理中可以直接取到该组件的this对象
3. ponent方式定义
extends一看就是ES6的类形式了,比较推荐使用
class Info ponent {
constructor(props) {
super(props);
this.state = {
name: this.props.name || 'myName'
showYear(e) {
console.log(this);
let elem = ReactDOM.findDOMNode(e.target);
console.log('year ' + elem.getAttribute('data-year'));
render() {
return &p onClick={this.showYear} data-year={this.props.year}&{this.state.name}&/p&
Info.defaultProps = {
year: new Date().getFullYear()
ReactDOM.render(&Info /&, document.getElementById('box'));
可以看到一些区别,初始化props与state的方式不一样
ES5形式中是直接在函数中return的方式,ES6形式的state是在构造函数中直接初始化this.state,而props初始化则需要在外部进行
再看看点击事件,会发现输出的this为null,因在ES6的类形式中,React并不会自动绑定函数方法的this对象,需要自行绑定
一般来说,有三种绑定方式
3.1 直接在构造函数中统一绑定
constructor(props) {
super(props);
this.state = {
name: this.props.name || 'myName'
this.showYear = this.showYear.bind(this);
3.2 直接在onClick中绑定
相对在构造函数中绑定来说,这种方法会更有针对性,不过多个统一绑定就会显得代码冗余
render() {
return &p onClick={this.showYear.bind(this)} data-year={this.props.year}&{this.state.name}&/p&
3.3 在onClick绑定中使用回调函数调用
render() {
return &p onClick={(e) =& this.showYear(e)} data-year={this.props.year}&{this.state.name}&/p&
这种方式需要手动传入event参数,而上述两种不需要
五、组件的生命周期
图片引自:组件的生命周期
React的组件有从产生到消亡,有个生命周期。宏观来讲有三个时期
1. 实例化期(Mounting)
实例化这个时期主要是组件的初始实例化阶段,如图
主要包括属性和状态的初始化阶段、组件即将加载(componentWillMount)阶段、组件渲染(render)阶段、组件加载完成(componentDidMount)阶段
除了render可在存在期的时候再次进行组件渲染之外,其他阶段只会发生一次
class Info ponent {
constructor(props) {
super(props);
this.state = {
name: this.props.name,
// 组件将加载
componentWillMount() {
console.log('componentWillMount: ', this.state.age)
// 组件加载完成
componentDidMount() {
console.log('componentDidMount: ', this.state.age)
render() {
console.log('Info render: ', this.state.age);
return &p&{this.state.name} {this.state.age}&/p&
ReactDOM.render(&Info name="Jack" /&, document.getElementById('box'));
2. 存在期间(Updating)
组件实例化之后,在组件存在的时期,随着与用户的交互,属性或状态的改变,组件可发生一些更新,如图
componentWillReceiveProps(nextProps)
组件接收到属性(通常是父级传来的),带一个参数,即为该属性对象
shouldComponentUpdate(nextProps, nextState)
组件是否应该更新,true|false,默认返回true,带两个参数,将要更新的属性对象和状态对象
需要注意的是,如果自定义了这个方法,就会直接覆盖默认的方法(若定义之后不返回则表示返回了false)
componentWillUpdate(nextProps, nextState)
组件将更新,带两个参数,将要更新的属性对象和状态对象
再次进入渲染阶段
componentDidUpdate(prevProps, prevState)
组件更新完成,带两个参数,之前(已经)更新的属性对象和状态对象
在这个时期,各个阶段按照流程不断地进行着,举个栗子
这里定义一个父组件InfoWrap和子组件Info
在实际开发中,为了防止JS阻塞HTML结构的渲染,初始异步获取数据时一般会放到componentDidMount中
class InfoWrap ponent {
constructor(props) {
super(props);
this.state = {
name: 'defaultName'
componentDidMount() {
setTimeout(() =& {
this.setState({
name: 'Jack'
setTimeout(() =& {
this.setState({
name: 'Jack'
render() {
console.log('InfoWrap render');
return &Info name={this.state.name} /&
ReactDOM.render(&InfoWrap /&, document.getElementById('box'));
通过setTimeout模拟异步,一段时间后改变状态state中的name值,通过属性name传入子Info组件中
这里要注意的是,两次setState的name值相同,
基于React依照state状态的diff来判断是否需要重新渲染数据,在InfoWrap中不会更新两次HTML,但还是会向子Info中传入两次属性props
class Info ponent {
constructor(props) {
super(props);
this.state = {
name: this.props.name,
increaseAge() {
this.setState({
age: this.state.age + 1
// 组件将加载
componentWillMount() {
console.log('componentWillMount: ', this.state.age)
// 组件加载完成
componentDidMount() {
console.log('componentDidMount: ', this.state.age)
// 组件接收到新的props
componentWillReceiveProps(nextProps) {
if (nextProps.name !== this.state.name) {
this.setState({
name: nextProps.name
console.log('componentWillReceiveProps: ', nextProps)
// 组件是否应该更新
shouldComponentUpdate(nextProps, nextState) {
console.log('shouldComponentUpdate: ', nextProps, nextState);
// return nextProps.name !== this.state.
return nextState.age !== 3;
// 组件将更新
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate: ', this.state.age)
// 组件更新完成
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate: ', this.state.age)
// 组件将移除
componentWillUnmount() {
console.log('componentWillUnmount: ', this.state.age)
render() {
console.log('Info render: ', this.state.age);
// 在这更改状态将会无限循环
// this.setState({
age: this.state.age + 1
return &p onClick={this.increaseAge.bind(this)} &{this.state.name} {this.state.age}&/p&
由上图,子Info被渲染了三次,而实际上第三次name并未改变,其实是不需要渲染的
在实际开发中,为了防止无意义的渲染,通常会在shouldComponentUpdate添加判断,自定义是否需要更新
将其中的return nextProps.name !== this.state.取消注释,则不再被更新渲染
细心点可以看到,Info组件中的setState是放在了componentWillReceiveProps中
为什么不直接在shouldComponentUpdate中判断是否需要更新后再更新状态呢?
根据上方的流程图,如果在这里更新,就会再次触发state改变,导致又多循环执行了一次
所以一般的做法是在componentWillReceiveProps中根据条件判断是否需要更新状态,然后在shouldComponentUpdate中再根据条件判断是否需要更新渲染组件
同理,千万不要在render的时候setState更新状态,这更危险,会出现死循环,不注意的话可以直接把浏览器搞崩了
以上是子组件从父组件获取数据后更新的情况,下面来看看在子组件中的自我更新(increaseAge方法)
假设现在点击一次age属性值自增一次,在age不等于3的时候才更新页面
可以看到,在render和componentDidUpdate阶段,state的值才被实实在在地更新了,所以在之前的阶段取setState之后的新值,仍为旧的值
3. 销毁期(Unmounting)
销毁期发生在组件被移除的时候,用于如果卸载组件后需要做一些特殊操作时,一般很少用
六、组件间的通信
组件一多起来,就涉及到不同组件之间的数据交流,主要有三种类型
1. 父子通信
React是单向的数据流动
父组件向子组件传递数据,其实就是通过props属性传递的方式,父组件的数据更新,通过props数据的流动,子组件也得到更新
2. 子父通信
子组件与父组件通信,不同于Angular.js的数据双向绑定,在React中默认支持子同步父的数据
若想实现父同步子的数据,则需要在子数据发生改变的时候,调用执行父props传来的回调,从而达到父的同步更新
class InputItem ponent {
constructor(props) {
super(props);
this.state = {};
inputChange(e) {
this.props.inputChange(e.target.value);
render() {
return &p title={this.props.title}&
[InputItem]-input: &input type="text" onChange={this.inputChange.bind(this)} /&
class Page ponent {
constructor(props) {
super(props);
this.state = {
inputValue: ''
inputChange(inputValue) {
this.setState({
inputValue,
render() {
&p&[Page]-input: &input type="input" value={this.state.inputValue} /&&/p&
&InputItem title="myInput" inputChange={this.inputChange.bind(this)} /&
&InputItem title="myInput" inputChange={this.inputChange.bind(this)} /&
ReactDOM.render(&Page /&, document.getElementById('box'));
这里定义了一个父组件Page,子组件InputItem
在父组件中&InputItem title="myInput" ... /& 其实就有了父与子的通信(props传递)
Page向InputItem传递了一个回调属性,InputItem数据改变后调用此回调,数据得到更新
3. 兄弟通信
上述是父同步子的数据,如果要实现兄弟之间(或者两个没什么关系的组件)的数据同步,就得结合父与子、子与父的方式
class InputItem ponent {
constructor(props) {
super(props);
this.state = {};
inputChange(e) {
this.props.inputChange(e.target.value);
render() {
return &p title={this.props.title}&
[InputItem]-input: &input type="text" onChange={this.inputChange.bind(this)} value={this.props.inputValue} /&
class Page ponent {
constructor(props) {
super(props);
this.state = {
inputValue: ''
inputChange(inputValue) {
this.setState({
inputValue,
render() {
&p&[Page]-input: &input type="input" value={this.state.inputValue} /&&/p&
&InputItem title="myInput" inputChange={this.inputChange.bind(this)} inputValue={this.state.inputValue} /&
&InputItem title="myInput" inputChange={this.inputChange.bind(this)} inputValue={this.state.inputValue} /&
ReactDOM.render(&Page /&, document.getElementById('box'));
子InputItem更新后,调用父Page的回调,在父Page中将更新后的数据通过props传至子InputItem
不同组件之间数据得到同步
4. 事件发布/订阅
这个还没用过 不清楚..
七、受控组件与非受控组件
在React中的表单Form系统中,有受控组件与非受控组件一说
1. 非受控组件
非受控,即表单项的value不受React的控制,不设初始value值,我们可以随意更改
但不便于统一使用React进行管理,也不便于设置初始值
class Page ponent {
constructor(props) {
super(props);
this.state = {
inputValue: ''
inputChange(e) {
console.log(e.target.value)
render() {
&p&&input type="input" onChange={this.inputChange.bind(this)} /&&/p&
ReactDOM.render(&Page /&, document.getElementById('box'));
可以看到,此input项目似乎与React没什么关系,想获取它的值就必须通过DOM获取到该元素,不方便管理
2. 受控组件
受控组件,是为了更好地管理表单项的值
但要注意的是,一旦设置了value,将不能通过直接在表单项输入就能改变value值
因为value已经被React控制,要更新value值,就得更新相应的state状态值
对于受控组件,又有初始值和值两种之分
2.1 初始值(defaultValue)
defaultValue这里指的是input,select,textarea等,相应的checkbox radio是defaultChecked
初始值只是初始的一个值,在第一次设置定义之后就不可改变
在实际开发中,数据的获取经常是异步的,大部分情况下会先初始设置input表单值为空,获取到数据后再放到input中(如编辑页面)
便会有以下代码
class InputItem ponent {
constructor(props) {
super(props);
this.state = {
inputValue: this.props.inputValue || ''
componentWillReceiveProps(nextProps) {
this.setState({
inputValue: nextProps.inputValue
inputChange(e) {
let inputValue = e.target.
console.log(inputValue);
// this.setState({
inputValue
render() {
return &p&&input type="input" onChange={this.inputChange.bind(this)} defaultValue={this.state.inputValue} /&&/p&
class Page ponent {
constructor(props) {
super(props);
this.state = {
inputValue: ''
componentDidMount() {
setTimeout(() =& {
this.setState({
inputValue: 'myValue'
render() {
return &InputItem inputValue={this.state.inputValue} /&
ReactDOM.render(&Page /&, document.getElementById('box'));
初始在InputItem中设置了defaultValue为空,一段时间后获取到父Page传来的新值inputValue,然而InputItem中的defaultValue并不会更新
这种情况,就不适用与defaultValue了,换成用状态控制的value即可
2.2 值(value)
render() {
return &p&&input type="input" onChange={this.inputChange.bind(this)} value={this.state.inputValue} /&&/p&
获取到异步的数据后,通过componentWillReceiveProps中更新状态值
加入onChange事件,在输入的时候更新状态值
而对于onChange事件的调用更新state,也有点点小技巧
假如input项目太多,为每个input定义一个change回调并不实际
这时可以在bind中指定参数,指定是某个input项,或者直接在input项中添加属性区分,调用的时候再获取
class InputItem ponent {
constructor(props) {
super(props);
this.state = {
userName: this.props.userName || '',
age: this.props.age || ''
componentWillReceiveProps(nextProps) {
this.setState({
userName: nextProps.userName,
age: nextProps.age
inputChange(name, e) {
this.setState({
[name]: e.target.value
// inputChange(e) {
this.setState({
[e.target.getAttribute('name')]: e.target.value
render() {
&p&&input type="input" name="userName" onChange={this.inputChange.bind(this, 'userName')} value={this.state.userName} /&&/p&
&p&&input type="input" name="age" onChange={this.inputChange.bind(this, 'age')} value={this.state.age} /&&/p&
class Page ponent {
constructor(props) {
super(props);
this.state = {
userName: '',
componentDidMount() {
setTimeout(() =& {
this.setState({
userName: 'Jack',
render() {
return &InputItem userName={this.state.userName} age={this.state.age} /&
ReactDOM.render(&Page /&, document.getElementById('box'));
默认情况下,如果bind中不填第二个参数,在回调中第一个参数就是触发的event对象
如果有第二个参数,回调中的第一个参数就是该参数,后续的参数才是触发的event对象
上述两个inputChange方法调用之后结果一样,这里也利用了ES6支持对象属性名为变量的新特性
另外,由于设置了value值之后的React组件表单项不能直接更改value值,需要修改state相应值。
在使用一些插件的时候可能会遇到问题,如日期插件bootstrap-datepicker
class DatePicker ponent {
constructor(props) {
super(props);
this.state = {
timeFrom: '',
timeEnd: ''
combindDate(date) {
let year = date.getFullYear(),
month = date.getMonth() + 1,
day = date.getDate();
month = month & 10 ? '0' + month :
day = day & 10 ? '0' + day :
return [year, month, day].join('-');
componentDidMount() {
let $timeFrom = $(this.refs.timeFrom);
$timeFrom.datepicker({
format: 'yyyy-mm-dd',
autoclose: true,
language: 'zh-CN'
}).on('changeDate', (ev) =& {
let day = ev.date.getDate();
if (day & 15) {
$timeFrom.datepicker('update', '');
// this.setState({
timeFrom: ''
// this.setState({
timeFrom: bindDate(ev.date)
render() {
&p&timeFrom: &input type="input" ref="timeFrom" value={this.state.timeFrom} /&&/p&
&p&timeEnd: &input type="input" ref="timeEnd"
value={this.state.timeEnd} /&&/p&
ReactDOM.render(&DatePicker /&, document.getElementById('box'));
且看看这个timeFrom,假设现在的需求是选择的日期不能大于15号
正常情况下,直接调用.datepicker('update', '');清空即可
但在React受控组件中,这关乎状态state值,所以要同时进行显示地setState(包括选成功的赋值与选失败的清空,即注释部分)
八、组件的复制
组件的复制也是一块知识,不过我这里应该不算是复制吧,其实只是一个具体的栗子
1. 弹窗中的组件并不是在弹窗之后才加载,其实是初始就加载
想象一下有这么一个需求:
有很多道题,每道题会有一些附加的文件,需要有个文件的轮播,另外点击文件还有弹窗预览,弹窗中下方是文件轮播,上方是文件的预览轮播
所以一个页面会出现多个相似的轮播,点击轮播中的文件可弹窗预览该文件,在弹窗中下方还有这个相似的轮播
所以要做的其实就是三个组件,页面组件,文件轮播组件,弹窗预览组件(该组件中使用一个文件轮播组件)
思路很清晰,不过在实现过程中发现,并不是想象的样子,弹窗中的文件轮播组件并不是在弹窗之后才加载,其实是页面加载出来就加载了。
那例子太复杂,用几个input项模拟一下吧
Page组件是页面组件,InputItem是共享的,BoxBanner是弹窗组件
class InputItem ponent {
constructor(props) {
super(props);
this.state = {
inputIndex: this.props.inputIndex || 0,
inputValue: this.props.inputValue || ''
componentWillReceiveProps(nextProps) {
this.setState({
inputIndex: nextProps.inputIndex,
inputValue: nextProps.inputValue
componentDidMount() {
console.log('componentDidMount ', this.state.inputIndex);
inputChange(e) {
this.setState({
inputValue: e.target.value
inputClick() {
console.log('inputClick');
render() {
return &p data-first="1" className="check-first"&{this.state.inputIndex}、
type="input"
onChange={this.inputChange.bind(this)}
onClick={this.inputClick.bind(this)}
value={this.state.inputValue}
style={{'margin': '10px'}}
class BoxBanner ponent {
constructor(props) {
super(props);
this.state = {
inputIndex: 0,
inputValue: ''
openBox(e) {
let elem = e.
if (elem.tagName !== 'BUTTON') {
this.setState({
inputIndex: elem.getAttribute('data-index'),
inputValue: elem.getAttribute('title')
layer.open({
title: false,
shadeClose: true,
// content: $('.template-box').html(),
content: $('.template-box'),
// content: $(this.refs.templateBox),
success: function(layero) {
let $first = $(layero).find('.check-first');
console.log('isFirst: ', $first.attr('data-first'));
$first.attr('data-first', '0');
}.bind(this),
end: function(layero) {
// $('.check-first').attr('data-first', '1');
render() {
&p onClick={this.openBox.bind(this)}&
&button data-index="1" title="box1"&box1&/button&
&button data-index="2" title="box1"&box2&/button&
&button data-index="3" title="box1"&box3&/button&
&div className="template-box" ref="templateBox" style={{display: 'none'}}&
&InputItem inputIndex={this.state.inputIndex} inputValue={this.state.title} /&
class Page ponent {
constructor(props) {
super(props);
render() {
&BoxBanner /&
ReactDOM.render(&Page /&, document.getElementById('box'));
这里有个要求是,判断是否是首次弹窗进来,初始设置data-first属性为1,弹窗后即更新为0
在BoxBanner组件中引入了一个InputItem组件,但InputItem组件被共享,只在页面开始加载是被加载了
传递到layer中的content似乎只是加载后的结果,可以看到isFirst值不是预想的
在layer的content中指定InputItem组件明显是不可行的,毕竟这是JSX
所以,就得在弹窗关闭之后恢复相关的值,即end回调中的注释部分
上述的代码中
// content: $('.template-box').html(),
content: $('.template-box'),
// content: $(this.refs.templateBox),
最开始用的是第一种方法,但这将只会传递html,其中的事件将不被执行
换成第二种,事件的传递得到解决,但在React中过多的DOM操作并不推荐,且如果存在多个.template-box时,基于弹窗中组件不会重新加载的问题,组件的获取就不正确
建议是换成第三种,取该组件的ref映射
Page组件中加多一项
render() {
&BoxBanner /&
&BoxBanner /&
以上就是这篇文章的全部内容了,希望大家能够喜欢。
这些内容可能对你也有帮助
更多可查看Javascript教程列表页。
猜您也会喜欢这些文章

我要回帖

更多关于 react native get请求 的文章

 

随机推荐