React入门

why React

React是Facebook开发的用来创建用户界面的JavaScript库,相当于MVC中的V(视图)。

目标

构建随着时间数据不断变化的大规模应用程序。

  • 简单。表达某一个时间点应用程序是个什么样子的,当底层数据改变后,React会决定是否更新用户界面且自动处理。
  • 声明式。数据变化后自会更新变化的部分。

主要原理

Virtual DOM

在web应用中,更新操作DOM通常比较昂贵且耗时,每次更新都可能导致重绘或重排。React通过抽象出一个js对象来模拟DOM,即Virtual DOM,来描述虚拟DOM是什么样子的(React使用render()来描述虚拟的DOM节点),并决定是否更新重绘真实的DOM。等到事件循环结束后,React会利用diff算法比较新旧Virtual DOM,计算出最快的方式更新真实DOM。

component

Virtual DOM的节点就是抽象、封装好的组件,通过React你唯一要做的事情就是构建组件。借助组件使React具有可组合性,关注界面分离。当组件的stateprops改变,React会比较新旧虚拟DOM,决定是否更新界面。

可以认为组件是简单的函数,接受 propsstate作为参数,然后渲染出HTML。组件只能渲染单个根节点,如果想要返回多个节点,它们必须被包含在同一个节点里。

组件其实是状态机(State Machine)。根据界面不同状态然后渲染这些状态,让用户界面和数据保持一致。
React里,只需更新组件的 state(使用唯一的this.setState(data, callback)方法),然后根据新的state 重新渲染用户界面,React 来决定如何最高效地更新 DOM。因此勿在组件里直接操作DOM。

以下是一个简单使用组合组件的带显示剩余输入字数的文本框例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var WordCount = React.createClass({
render: function() {
return (
<p className="text-right">{this.props.remain}/{this.props.total}</p>
)

}
});
var TextAreaCount = React.createClass({
getInitialState: function() {
return {remain: this.props.total}
},
getDefaultProps: function() {
return {
total: 100
};
},
handleChange: function(event) {
var total = this.props.total,
elm = event.target,
remain = total - elm.value.length;
if(remain < 0) {
remain = 0;
elm.value = elm.value.substring(0, total);
}
this.setState({remain: remain});
},
render: function() {
var remain = this.state.remain;
return (
<div>
<textarea onChange={this.handleChange} className="form-control" />
<WordCount remain={remain} total = {this.props.total} />
</div>
);

}
})
React.render(
<TextAreaCount />,
document.getElementById('example')
);

组件的生命周期

组件生命周期包含3个阶段

  • Mounting,组件正添加到DOM
  • Updating,组件正重新渲染
  • Unmounting,组件正从DOM移除

Mounting

  • getInitialState(),在组件添加到DOM前执行,初始化状态。
  • componentWillMount(),在Mounting前执行
  • componentDidMount(),在Mounting后执行,即组件添加到DOM完成后。子组件先于父亲执行。

Unpdating

  • componentWillReceiveProps(object nextProps),当一个已经mounted的组件接收新的属性时,可使用该方法判断新旧值,使用this.setState()初始化
1
var MyComponent3 = React.createClass({
  getInitialState: function() {
      return {likesIncreasing: false};
  },
  componentWillReceiveProps: function(nextProps) {
    this.setState({
      likesIncreasing: nextProps.likeCount > this.props.likeCount
    });
  },
  render: function() {
    var likesIncreasing = this.state.likesIncreasing? 'likes increasing':''
    return (
      <div>
        <p>current like count:{this.props.likeCount}. {likesIncreasing}</p>
      </div>
    );
  }
});

React.render(
  <MyComponent3 likeCount='1'/>,
  document.getElementById('example3')
);
setTimeout(function() {
    React.render(
      <MyComponent3 likeCount='2'/>,
      document.getElementById('example3')
    );
},1000)
  • shouldComponentUpdate(object nextProps, object nextState):boolean,返回false时,不更新dom。当使用forceUpdate时此方法不执行。
  • componentWillUpdate(object nextProps, object nextState),不能使用this.setState()
  • componentDidUpdate(object prevProps, object prevState)

Unmounting

  • componentWillUnmount()

refs 和 findDOMNode()

若你想引用DOM节点,可以使用React.findDOMNode(component),但此方法只限该节点已添加到DOM中,因此在render()使用该方法将报错。

除了使用this获取当前的组件,也可以使用refs来获取节点。

1
var MyComponent1 = React.createClass({
  handleChange: function() {
    var reg = new RegExp(this.props.reg);
    var myTextInput = React.findDOMNode(this.refs.myTextInput);
    var helpText = React.findDOMNode(this.refs.helpText);
        $(helpText).css('display','none');
    if(!reg.test(myTextInput.value)) {
      $(helpText).css('display','');
    }
  },
  render: function() {
    return (
      <div>
        <input type="text"
          className="form-control"
          ref="myTextInput"
          {...this.props}
          onChange={this.handleChange}
          />
        <label ref="helpText" className="help-block" style={{display:"none"}}>{this.props.msg}</label>
      </div>
    );
  }
});

React.render(
  <MyComponent1 reg='^\d*$' msg='please input number' />,
  document.getElementById('example1')
);

state工作原理

React 把用户界面当作简单状态机。把用户界面想像成拥有不同状态然后渲染这些状态,可以轻松让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。React 来决定如何最高效地更新 DOM。

哪些组件需要使用state

但是,有时需要对用户输入、服务器请求或者时间变化等作出响应,这时才需要使用 State。

尝试把尽可能多的组件无状态化。 这样做能隔离 state,把它放到最合理的地方,也能减少冗余并,同时易于解释程序运作过程。

常用的模式是创建多个只负责渲染数据的无状态(stateless)组件,在它们的上层创建一个有状态(stateful)组件并把它的状态通过 props 传给子级。这个有状态的组件封装了所有用户的交互逻辑,而这些无状态组件则负责声明式地渲染数据。大部分组件的工作应该是从 props 里取数据并渲染出来。

需要使用state情况:

  • 对用户输入进行响应
  • 服务器请求
  • 时间变化的响应

State 应该包括那些可能被组件的事件处理器改变并触发用户界面更新的数据。 ,尽可能使用 props 来作为惟一数据来源。把 props 保存到 state 的一个有效的场景是需要知道它以前值的时候,因为未来的 props 可能会变化。