Entry to React

原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大多数教程专业、系统。

1.1 Introduction to React

1.1.1 What is React?

React IS A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES , INCLUDING

  1. React.js
  2. ReactRenders: ReactDOM / ReactServer / ReactCanvas
  3. Flux模式及其实现
  4. React Components
  5. React Native
  6. GraphQI + Relay

1.1.2 Basic concept in React

  1. React.js
    React.js 是 React 的核心库,在应用中必须先加载核心库
    
    
  1. ReactDOM.js
    DOM渲染器,为了在 web 页面中显示开发的组件,需要调用 ReactDOM.render 方法, 第一个参数是 React 组件,第二个参数为 HTMLElement。
  2. JSX
    JSX 是 React 自定义的语法,最终 JSX 会转化为 JS 运行于页面当中
  3. Components
    Component是 React 中的核心概念,页面当中的所有元素都是通过 React 组件来表达。
  4. Virtual DOM
    React 抽象出来的虚拟 DOM 树,虚拟树是 React 高性能的关键。
  5. one-way reactive data flow
    React 应用的核心设计模式,数据流向自顶向下

1.1.3 Features of React

  1. Component的组合模式
    组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
    React 就是基于组合模式, 无论是应用等级还是一个表单亦或是一个按钮都视为一个组件, 然后基于组件的组合构建整个应用,这样的结构一直是前端界想要却迟迟不来的 web component。
    基于组合模式的优点:
  • 构建可重用的组件:组件的开发能够形成公司的组件库,每个业务的开发都能积累可重用的组件。
  • 无学习障碍:天然符合 HTML 的结构, 对前端开发者来说几乎没有学习障碍。
  • 具有弹性的架构:组合模式很简单却很有效,能够构建简单的页面也能构建大型的前端应用。
  • 源码高可维护性:开发只是工作中的一部分,应用的上线才是噩梦的开始,很多大型应用因为复制的业务逻辑导致无法快速响应业务需求,可维护性低。
  1. One way data flow
    Javascript 是脚本语言,不能像静态语言一样通过编译定位为题,想要清晰的定位到应用中的 bug 需要深入了解业务代码,对于大型前端应用来说,因为业务代码量很大且复杂,很难定位到 bug。 然而 React 的单向数据流的设计让前端 bug 定位变得简单, 页面的 UI 和数据的对应是唯一的,我们可以通过定位数据变化就可以定位页面展现问题。
  2. High efficiency
    基于VirtualDOM算法可以让只有需要改变的元素才去重渲染
  3. Separate frame design
    React.js 现在的版本已经将源码分开为 ReactDOM 和 React.js . 这就意味着 React 不仅仅能够在 web 端工作, 甚至可以在服务端(nodejs),Native 端运行。
    与此同时, 我们可以自定义自己的渲染器, 实现比如 Three.js, Pixi.js, D3.js 的 React 方式渲染。

1.1.4 Where can React apply?

  1. web 端
  2. 原生应用
    IOS、Android、Native 应用
    这要归功于 facebook 开源的 React Native。 基于 React Native , 我们将可以使用 jsx 来实现具有原生应用性能的 UI 运行于 IOS 和 android 中,同时我们也可以通过 NW.js 或者 Electron 来实现基于 React 的桌面应用。
  3. 服务器端渲染
    React 除了在 Web 和 Native 环境以外, 也可以通过 React 实现在服务器端渲染出 HTML。

1.2 JSX Grammar

1.2.1 Prepare running environment

  1. Babel REPL
    Babel REPL
    直接在 REPL 中写 JSX 语法,可以实时的查看编译后的结果。
  2. JSFiddle
    在线模式 React Fiddle
  3. Local development

1.2.2 JSX Grammar

创建 JSX 语法的本质目的是为了使用基于 xml 的方式表达组件的嵌套,保持和 HTML 一致的结构,语法上除了在描述组件上比较特别以外,其它和普通的 Javascript 没有区别。 并且最终所有的 JSX 都会编译为原生 Javascript。

  1. jsx componments
    JSX 组件分为 HTML 组件和 React 组件
    HTML 组件就是 HTML 中的原生标签, 如:
function render() {
       return  

hello, React World

} function render() { return
  • list item 1
  • list item 2
}

React 组件就是自定义的组件,如

// 定义一个自定义组件
var CustomComponnet = React.createClass({
        render: function() {
            return 
custom component
} }); // 使用自定义组件 function render() { return

}
  1. properties of jsx components
    和 html 一样,JSX 中组件也有属性,传递属性的方式也相同
    对于 HTML 组件
 function render() {
      return  

hello, React, world

}

如果是 React 组件可以定义自定义属性,传递自定义属性的方式:

  function render() {
      return 

} }

属性即可以是字符串,也可以是任意的 Javascript 变量, 传递方式是将变量用花括号, eg:

  function render() {
      var data = {a: 1, b:2};
      return 

}

需要注意的地方上,属性的写法上和 HTML 存在区别,在写 JSX 的时候,所有的属性都是驼峰式的写法,主要是出于标准的原因,驼峰式是 Javascript 的标准写法,并且 React 底层是将属性直接对应到原生 DOM 的属性,而原生 DOM 的属性是驼峰式的写法,这里也可以理解为什么类名不是 class 而是 className 了, 又因为 class 和 for 还是 js 关键字,所以 jsx 中:

class => className
for => htmlFor

除此之外比较特殊的地方是 data-*aria-*两类属性是和 HTML 一致的。

  1. curly braces
  • 显示文本
  function render() {
        var text = "Hello, World"
        return 

{text}

}
  • 运算
funtion render() {
      var text = text;
      var isTrue = false;
      var arr = [1, 2, 3];
      return 

{text} {isTrue ? "true" : "false"} {arr.map(function(it) { return {it} })}

}
  1. 限制规则
    render 方法返回的组件必须是有且只有一个根组件,错误情况的例子
  // 无法编译通过,JSX 会提示编译错误
  function render() {
    return (

....

....

) }
  1. 组件命名空间
    JSX 可以通过命名空间的方式使用组件, 通过命名空间的方式可以解决相同名称不同用途组件冲突的问题。如:
  function render() {
    return 

}

1.2.3 Understand JSX

  1. JSX 的编译方式
    • 在 HTML 中引入 babel 编译器, 如上 Hello World 程序中一样。
    • 离线编译 JSX,通过 babel 编译 JSX,细节我们将在第二章中讲解。
  2. JSX 到 JS 的转化
    Hello World 程序转化为 JS 的代码如下:
  var Hello = React.createClass({
       displayName: 'Hello',
       render: function() {
          return React.createElement("div", null, "Hello ", this.props.name);
     }
  });
  ReactDOM.render(
       React.createElement(Hello, {name: "World"}),
       document.getElementById('container')
  );

1.3 React Components

1.3.1 Create a component

  • 创建一个 React 组件的方法为,调用 React.createClass 方法,传入的参数为一个对象,对象必须定义一个 render 方法,render 方法返回值为组件的渲染结构,也可以理解为一个组件实例(React.createElement 工厂方法的返回值),返回值有且只能为一个组件实例,或者返回 null/false,当返回值为 null/false 的时候,React 内部通过
var MyComponent = React.createClass({
        render: function() {
            return 

....

; } });
  • Component命名空间
    可以看出 React.createClass 生成的组件类为一个 Javascript 对象。 当我们想设置命名空间组件时,可以在组件下面添加子组件
 MyComponent.SubComponent = React.createClass({...});
 MyComponent.SubComponent.Sub = React.createClass({....});
  • 无状态组件
    除了可以通过 React.createClass 来创建组件以外,组件也可以通过一个普通的函数定义,函数的返回值为组件渲染的结果
function StatelessComponent(props) {
        return  
Hello {props.name}
}

无状态组件能够优化性能,因为其内部不会维护状态,React 内部不会有一个对应的组件实例,并且也没有生命周期 hook

1.3.2 将组件渲染到 DOM 中

当创建好了组件过后,为了将组件渲染到 DOM 中显示出来,需要两个步骤:

  1. 在 HTML 中定义一个元素,设置 id 属性
  2. JSX 中调用 ReactDOM.render 方法, 第一个参数为 组件,第二个为刚才定义的 DOM 元素

1.3.3 States of components

React 的渲染结果是由组件属性和状态共同决定的,状态和属性的区别是,状态维护在组件内部,属性是由外部控制,我们先介绍组件状态相关细节:
控制状态的 API 为:

  • this.state:组件的当前状态
  • getInitialState:获取组件的初始状态,在组件加载的时候会被调用一次,返回值赋予 this.state 作为初始值
  • this.setState:组件状态改变时,可以通过 this.setState 修改状
    • setState 方法支持按需修改,如 state 有两个字段,仅当 setState 传入的对象包含字段 key 才会修改属性
  • 每次调用 setState 会导致重渲染调用 render 方法
  • 直接修改 state 不会重渲染组件
 var Switch = React.createClass({
        // 定义组件的初始状态,初始为关
        getInitialState: function() {
            return {
                open: false
            }
        },
        // 通过 this.state 获取当前状态
        render: function() {
            console.log('render switch component');
            var open = this.state.open;
            return 
        },
        // 通过 setState 修改组件状态
        // setState 过后会 React 会调用 render 方法重渲染
        toggleSwitch: function() {
            var open = this.state.open;
            this.setState({
                open: !open
            });
        }
    })

1.3.4 Properties of components

前面已经提到过 React 组件可以传递属性给组件,传递方法和 HTML 中无异, 可以通过 this.props 获取组件属性
属性相关的 API 为:

  • this.props: 获取属性值
  • getDefaultProps: 获取默认属性对象,会被调用一次,当组件类创建的时候就会被调用,返回值会被缓存起来,当组件被实例化过后如果传入的属性没有值,会返回默认属性值
  • this.props.children:子节点属性
  • propTypes: 属性类型检查
// props.name 表示代办事项的名称
    var TodoItem = React.createClass({
        render: function() {
            var props = this.props;
            return 
{props.name}
} }); ReactDOM.render( , document.getElementById('example'));
  1. children属性
    组件属性中会有一个特殊属性 children ,表示子组件, 还是以上面一个组件为例子,我们可以换一种方式定义 name:
var TodoItem = React.createClass({
        render: function() {
            var props = this.props;
            return 
{props.children}
} }); ReactDOM.render( 代办事项1, document.getElementById('example'));
  1. 属性类型检查
    为了保证组件传递属性的正确性, 我们可以通过定义 propsType 对象来实现对组件属性的严格校验
var MyComponent = React.createClass({
        propTypes: {
            optionalArray: React.PropTypes.array,
            optionalBool: React.PropTypes.bool,
            optionalFunc: React.PropTypes.func,
            optionalNumber: React.PropTypes.number,
            optionalObject: React.PropTypes.object,
            optionalString: React.PropTypes.string,
            // 任何可以被渲染的包括,数字,字符串,组件,或者数组
            optionalNode: React.PropTypes.node,
            // React 元素
            optionalElement: React.PropTypes.element,
            // 枚举
            optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
            // 任意一种类型
            optionalUnion: React.PropTypes.oneOfType([
              React.PropTypes.string,
              React.PropTypes.number,
              React.PropTypes.instanceOf(Message)
            ]),
            // 具体类型的数组
            optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
            // 具体类型的对象
            optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
            // 符合定义的对象
            optionalObjectWithShape: React.PropTypes.shape({
              color: React.PropTypes.string,
              fontSize: React.PropTypes.number
            }),
            requiredFunc: React.PropTypes.func.isRequired,
            requiredAny: React.PropTypes.any.isRequired,
            // 自定义校验
            customProp: function(props, propName, componentName) {}
        }
    });
  1. 属性传递的单向性
    我们已经提到过 React 的单向数据流模式,数据的流动管道就是 props,流动的方向就是组件的层级自定向下的方向。所以一个组件是不能修改自身的属性的,组件的属性一定是通过父组件传递而来(或者默认属性)。
  2. 无状态组件属性
    对于无状态组件,可以添加 .propTypes 和 .defaultProps 属性到函数上。

1.3.5 组件的嵌套组合

在JSX 实例子中,当我们循环输出 todo 列表的时候,React 会提示对于循环输出的组件,需要有一个唯一的 key 属性。这个问题的原因在于 React 的调和机制(Reconciliation)上。

  1. 什么叫调和?
    在每次数据更新过后,React 会重新调用 render 渲染出新的组件结构,新的结构应用到 DOM 中的过程就叫做调和过程。
  2. 为什么需要调和?
    假设我们有一个输入组件,这个时候我们正聚焦在输入框中,当修改值过后触发事件导致了数据改变,数据改变导致了重渲染, 这个时候输入框被替换成了新的 DOM。 这个过程对用户来说应该是无感知的,所以那原来的聚焦状态应该被保存, 那怎么做到的呢? DOM 都被替换了,输入状态,选择状态为什么还能保存。 我们先不急着知道 How,目前只需要知道这就是调和过程。
    除了保存状态以外,调和过程还做了很多 DOM 优化。 比如输出一个数组的时候,数据新增加或者减少了一下,或者数组项值改变了,实际上我们没有必要删除原来的 DOM 结构,只需要修改 DOM 的值或者删除 DOM 就能实现重渲染。
    这就是为什么要有 key 属性,key 属性能够帮助定位 DOM 与数组元素的关系,在重渲染的时候能够实现渲染优化。

1.4 Life circle of React Components

14.1 Components

React 中组件有自己的生命周期方法,简单理解可以为组件从出生(实例化) -> 激活 -> 销毁生命周期 hook。通过这些 hook 方法可以自定义组件的特性。 除此之外,还可以设置一些额外的规格配置。

Entry to React_第1张图片
组件生命周期.png
这些生命周期方法都可以在调用 React.createClass 的参数对象中传入, 之前使用过了一些方法:
render getInitialState getDefaultProps propTypes

1.4.2 mixins

Type: array mixins
mixins 可以理解为 React 的插件列表,通过这种模式在不同组件之间共享方法数据或者行为只需共享 mixin 就行,mixins 内定义的生命周期方法在组件的生命周期内都会被调用。

var MyMixin1 = {
    componentDidMount: function() {
        console.log('auto do something when component did mount');
    }
};

var MyMixin2 = {
    someMethod: function() {
        console.log('doSomething');
    }
};

var MyComponnet = React.createClass({
    mixins: [MyMixin1, MyMixin2],
    componentDidMount: function() {
        // 调用 mixin1 共享的方法
        this.someMethod();
    }
});

1.4.3 statics

Type:object statics
statics可以定义组件的类方法
React 的组件是 面向对象OOP 的思维,MyComponent 是一个 class,class 分为类方法和实例方法,实例方法可以访问 this, 然而类方法不能,所以我们不能在 Class 中返回状态或者属性。

var MyComponent = React.createClass({
  statics: {
    customMethod: function(foo) {
      return foo === 'bar';
    }
  }
});

MyComponent.customMethod('bar');  // true

1.4.4 displayName

Type: string displayName
为了显示调试信息,每个组件都会有一个名称,JSX 在转为 JS 的时候自动的设置 displayName,当然我们也可以自定义 displayName

// Input (JSX):
var MyComponent = React.createClass({ });

// Output (JS):
var MyComponent = React.createClass({displayName: "MyComponent", });

1.4.5 生命周期方法

Entry to React_第2张图片
组件生命周期.png

1.4.6 componentWillMount

void componentWillMount()

  • 条件:第一次渲染阶段在调用 render 方法前会被调用
  • 作用:该方法在整个组件生命周期只会被调用一次,所以可以利用该方法做一些组件内部的初始化工作

1.4.7 componentDidMount

void componentDidMount()

  • 条件:第一次渲染成功过后,组件对应的 DOM 已经添加到页面后调用
  • 作用:这个阶段表示组件对应的 DOM 已经存在,我们可以在这个时候做一些依赖 DOM 的操作或者其他的一些如请求数据,和第三方库整合的操作。如果嵌套了子组件,子组件会比父组件优先渲染,所以这个时候可以获取子组件对应的 DOM。

1.4.8 componentWillReceiveProps(newProps)

void componentWillReceiveProps(
   object nextProps
)
  • 条件: 当组件获取新属性的时候,第一次渲染不会调用
  • 用处: 这个时候可以根据新的属性来修改组件状态
    componentWillReceiveProps: function(nextProps) {
       this.setState({
           likesIncreasing: nextProps.likeCount > this.props.likeCount
      });
    }

注意: 这个时候虽说是获取新属性,但并不能确定属性一定改变了,例如一个组件被多次渲染到 DOM 中,如下面:

    var Component = React.createClass({
        componentWillReceiveProps: function(nextProps) {
            console.log('componentWillReceiveProps', nextProps.data.bar);
        },
        rener: function() {
            return 
{this.props.data.bar}
} }); var container = document.getElementById('container'); var mydata = {bar: 'drinks'}; ReactDOM.render(, container); ReactDOM.render(, container); ReactDOM.render(, container);

结果会输出两次 componentWillReceiveProps,虽然属性数据没有改变,但是仍然会调用 componentWillReceiveProps 方法。

1.4.9 shouldComponentUpdate(nextProps, nextState)

boolean shouldComponentUpdate(
   object nextProps, object nextState
)
  • 条件: 接收到新属性或者新状态的时候在 render 前会被调用(除了调用 forceUpdate 和初始化渲染以外)
  • 用处: 该方法让我们有机会决定是否重渲染组件,如果返回 false,那么不会重渲染组件,借此可以优化应用性能(在组件很多的情况)。

1.4.10 componentWillUpdate

void componentWillUpdate(
  object nextProps, object nextState
)
  • 条件:当组件确定要更新,在 render 之前调用
  • 用处:这个时候可以确定一定会更新组件,可以执行更新前的操作
  • 注意:方法中不能使用 setState ,setState 的操作应该在 componentWillReceiveProps 方法中调用

1.4.11 componentDidUpdate

void componentDidUpdate(
  object prevProps, object prevState
)
  • 条件:更新被应用到 DOM 之后
  • 用处:可以执行组件更新过后的操作

1.4.12 生命周期与单向数据流

我们知道 React 的核心模式是单向数据流,这不仅仅是对于组件级别的模式,在组件内部 的生命周期中也是应该符合单向数据的模式。数据从组件的属性流入,再结合组件的状态,流入生命周期方法,直到渲染结束这都应该是一个单向的过程,其间不能随意改变组件的状态。
Entry to React_第3张图片
组件内部数据流.png

1.5 React & DOM

1.5.1获取DOM元素

DOM真正被添加到HTML中的hook为

  • componentDidMount
  • componentDidUpdate

在这两个 hook 函数中, 我们可以获取真正的 DOM 元素,React 提供的获取方法两种方式

  1. findDOMNode()
    通过 ReactDOM 提供的 findDOMNode 方法, 传入参数
var MyComponent = React.createClass({
    render: function() {
        return 
....
}, componentDidMount: function() { var $root = ReactDOM.findDOMNode(this); console.log($root); } })

需要注意的是此方法不能应用到无状态组件上

  1. Refs
    上面的方法只能获取到 root 元素,那如果我的 DOM 有很多层级,我想获取一个子级的元素呢?React 提供了 ref 属性来实现这种需求。
    每个组件实例都有一个 this.refs 属性,会自动引用所有包含 ref 属性组件的 DOM
var MyComponent = React.createClass({
    render: function() {
        return  
}, componentDidMount: function() { var $btn = this.refs.btn; var $link = this.refs.link; console.log($btn, $link); } })

1.5.2 DOM事件

  1. 绑定事件
    在 React 中绑定事件的方式很简单,只需要在元素中添加事件名称的属性已经对应的处理函数,如:
var MyComponent = React.creatClass({
    render: function() {
        return  
}, onClick: function() { console.log('click me'); } });

事件名称和其他属性名称一样,服从驼峰式命名。

  1. 合成事件(SyntheticEvent)
    在 React 中, 事件的处理由其内部自己实现的事件系统完成,触发的事件都叫做 合成事件(SyntheticEvent),事件系统对浏览器做了兼容,其提供的 API 与原生的事件无异。
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
DOMEventTarget target
number timeStamp
string type

和原生事件的区别在于,事件不能异步话,如:

function onClick(event) {
     console.log(event); // => nullified object.
     console.log(event.type); // => "click"
     var eventType = event.type; // => "click"

     setTimeout(function() {
         console.log(event.type); // => null
        console.log(eventType); // => "click"
  }, 0);

     this.setState({clickEvent: event}); // Won't work. this.state.clickEvent will only contain null values.
     this.setState({eventType: event.type}); // You can still export event properties.
}

原因是在事件系统的内部实现当中, 一个事件对象可能会被重用(也就是事件做了池化 Pooling)。当一个事件响应函数执行过后,事件的属性被设置为 null, 如果想用保持事件的值的话,可以调用
event.persist()
这样,属性会被保留,并且事件也会被从池中取出。

  1. 事件捕获和冒泡
    在 DOM2.0 事件分为捕获阶段和冒泡阶段,React 中通常我们注册的事件为冒泡事件,如果要注册捕获阶段的事件,可以在事件名称后加 Capture 如:
onClick
onClickCapture
  1. 支持事件列表
粘贴板事件 {
      事件名称:onCopy onCut onPaste
      属性:DOMDataTransfer clipboardData
}
编辑事件 {
      事件名称:onCompositionEnd onCompositionStart onCompositionUpdate
      属性:string data
}
键盘事件 {
      事件名称:onKeyDown onKeyPress onKeyUp
      属性: {
        boolean altKey
        number charCode
        boolean ctrlKey
        boolean getModifierState(key)
        string key
        number keyCode
        string locale
        number location
        boolean metaKey
        boolean repeat
        boolean shiftKey
        number which
    }
}
// 焦点事件除了表单元素以外,可以应用到所有元素中
焦点事件 {
      事件名称:onFocus onBlur
      属性:DOMEventTarget relatedTarget
}
表单事件 {
      事件名称:onChange onInput onSubmit
}
鼠标事件 {
      事件名称:{
        onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp
    }
      属性:{
        boolean altKey
        number button
        number buttons
        number clientX
        number clientY
        boolean ctrlKey
        boolean getModifierState(key)
        boolean metaKey
        number pageX
        number pageY
        DOMEventTarget relatedTarget
        number screenX
        number screenY
        boolean shiftKey
    }
}
选择事件 {
      事件名称:onSelect
}
触摸事件 {
      事件名称:onTouchCancel onTouchEnd onTouchMove onTouchStart
      属性:{
        boolean altKey
        DOMTouchList changedTouches
        boolean ctrlKey
        boolean getModifierState(key)
        boolean metaKey
        boolean shiftKey
        DOMTouchList targetTouches
        DOMTouchList touches
    }
}
UI 事件 {
      事件名称:onScroll
      属性:{
        number detail
        DOMAbstractView view
    }
}
滚轮事件 {
      事件名称:onWheel
      属性:{
        number deltaMode
        number deltaX
        number deltaY
        number deltaZ
    }
}
媒体事件 {
      事件名称:{
        onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting
    }
}
图像事件 {
      事件名称:onLoad onError
}
动画事件 {
      事件名称:onAnimationStart onAnimationEnd onAnimationIteration
      属性:{
        string animationName
        string pseudoElement
        float elapsedTime
    }
}
渐变事件 {
      事件名称:onTransitionEnd
      属性: {
        string propertyName
        string pseudoElement
        float elapsedTime
    }
}

1.5.3 表单事件

在 React 中比较特殊的事件是表单事件,大多数组件都是通过属性和状态来决定的,但是表单组件如 input, select, option 这些组件的状态用户可以修改,在 React 中会特殊处理这些组件的事件。

  1. onChange 事件
    和普通 HTML 中的 onChange 事件不同, 在原生组件中,只有 input 元素失去焦点才会触发 onChange 事件, 在 React 中,只要元素的值被修改就会触发 onChange 事件。
var MyComponent = React.createClass({
    getInitialState: function() {
        return {
            value: ''
        }
    },
    render: function() {
        return  
}, onChange: function(ev) { console.log('change: ' + ev.target.value); this.setState({ value: ev.target.value }); }, // onChange 事件支持所有组件,可以被用于监听冒泡事件 onChangeBubble: function(ev) { console.log('bubble onChange event', + ev.target.value); } })
  1. 交互属性
    表单组件中能被用户修改的属性叫交互属性,包括:
value => 
selected => 
  1. textarea
    在 HTML 中,textarea 的值是像如下定义的:

而在 React 中, TextArea 的使用方式同 input 组件,使用 value 来设置值

var MyComponent = function() {
    render: function() {
        return