react入门 (2)

  02 Jan 2016


ReactDOM.render()

ReactDOM.render() 是react的基本用法,是将模板语言转换成html语言,并插入指定的dom节点。

  ReactDOM.render(
    <h1>hello world!</h1>,
    document.getElementById('example')
  )

上面代码将一个 h1 标题,插入 example 节点(查看 react-demos edemo01)

JSX语法

HTML语言直接写在js语言中,不加任何引号,这就是jsx的语法,它允许HTML与JavaScript的混写。

 var names=['嘎嘎','呱呱','吉吉'];
 ReactDOM.render(
 <div>
 {
    names.map(function(name){
    return <div>hello {name}</div>
    })
 }
 </div>,
 document.getElementById('example')
 );
 

上面代码体现了jsx基本语法规则:遇到html标签,就用html规则解析;遇到代码块以{开头,就用JavaScript规则解析。

jsx允许直接在模板插入JavaScript变量。如果这个变量是一个数组,则会展开这个数组所有成员。

 var names=['嘎嘎','呱呱','吉吉'];
 ReactDOM.render(
 <div>{names}
 </div>,
 document.getElementById('example')
 );
 

上面的代码names 变量是个数组,结果jsx会把所有成员添加到模板。

#### 组件

React 允许将代码封装成组件(component),然后像插入普通HTML标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类。

 var HelloMessage=React.createClass({
  render: function(){
  return <h1>hello {this.props.name}</h1>;
  }
 });
 ReactDOM.render(
 <HelloMessage name="hehe" />,
 document.getElementById('example')
 );

上面的代码中,变量HelloMessage 就是一个组件类。模板插入时,会自动生成HelloMessage 的一个实例。所有的组件类都必须有

自己的render 方法,用于输出组件。

注意!组件类的第一个字母必须大写,否则会报错,比如HelloMessage 不能写成helloMessage。另外,组件类只能包含一个顶层标签,否则也会报错。

 var HelloMessage = React.createClass({
   render: function() {
     return <h1>
       Hello {this.props.name}
     </h1><p>
       some text
     </p>;
   }
 });

上面代码会报错,因为HelloMessage组件包含了两个顶层标签:h1和p。 组件的用法与原生的 HTML 标签完全一致,可以任意加入属性,比如 ,就是 HelloMessage 组件加入一个 name 属性,值为 John。组件的属性可以在组件类的 this.props 对象上获取,比如 name 属性就可以通过 this.props.name 读取

添加组件属性,有一个地方需要注意,就是 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。

this.props.children

this.props 对象的属性与组件的属性一一对应,但是一个例外,就是this.props.children 属性,它表示组件的所有子节点。

var NotesList=React.createClass({
render:function(){
return (
    <ol>
    {
    React.Children.map(this.props.children,function(child){
    return <li>{child}</li>;

    })
    }
    </ol>

);


}

});
ReactDOM.render(
   <NotesList>
   <span>i</span>
   <span>love</span>
   <span>you</span>
   </NotesList>,
   document.body
);

上面代码的 NotesList 组件有三个span 子节点,他们都可以通过 this.props.children 读取。

这里需要注意,this.props.children 的值有三种可能:当前组件没有子节点 他就是 undefind;如果有一个子节点,数据类型是object;如果有多个子节点数据类型就是 array 。所以 处理this.props.children 的时候要小心。

React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是object。

PropTypes

组件的属性可以接受任意值,字符串,对象,函数等等都可以。有时有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。

组件类的 PropTypes属性,就是用来验证组件实例的属性是否符合要求

var MyTitle=React.createClass({
propTypes:{
  title:React.PropTypes.string.isRequired,
},
render:function(){
 return <h1> {this.props.title} </h1>;
}
});

上面的MyTitle组件有一个title 属性。PropTypes 告诉React,这个 title 属性是必须的,而且它的值必须是字符串。现在我们设置 title 为一个数值。

var data=123;
ReactDOM.render(
<MyTitle title={data}/>,
document.body
);

这样一来,title属性就通不过验证了。控制台会显示一行错误信息。

Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.

此外,getDefaultProps 方法可以用来设置组件属性的默认值。

 var Title=React.createClass({
 getDefaultProps: function(){
 return {
   title:'hello world'
  };
 },
 render:function(){
  return <h1>{this.props.title}</h1>
  }
 });
 ReactDOM.render(
 <Title/>,
 document.body
 );

会输出hello world。

获取真实的DOM节点

组件并不是真实的DOM节点,而是存在于内存之中的一种数据结构,叫做虚拟DOM。只有当它插入文档之后,才会变成真实的dom。根据React的设计, 所有的dom变动都先在虚拟的DOM上发生,然后再将实际发生变动的部分,反映到真实的Dom上,这种算法叫做DOM diff,它可以极大提高网页性能表现。

但是有时候需要从组件获取真实DOM的节点,这时就要用到 ref 属性。

var MyComponent=React.createClass({
handleClick:function(){
this.refs.myTextInput.focus();
},
render:function(){
return (
  <div>
  <input type="text" ref="myTextInput" />
  <input type="button" value="Focus the text input" onclick={this.handleClick}/>
  </div>
 )

}
});
ReactDOM.render(
    <MyComponent />,
    document.getElementById('example')
)

上面代码中,组件MyComponent 的子节点有一个文本框,用于获取用户的输入。这时就必须获取真实的dom节点,虚拟dom是拿不到用户输入的,为了做到这一点。文本框有一个ref 属性,然后this.refs.[refName] 就会返回这个真实的DOM 节点。

需要注意的是,由于 this.refs.[refName] 属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取 this.refs.[refName] 属性。 React 组件支持很多事件,除了 Click 事件以外,还有 KeyDown 、Copy、Scroll 等,完整的事件清单请查看官方文档。

this.state

组件免不了要与用户互动,react的一大创新,就是将组件看成一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染UI.

 var LikeButton=React.createClass({
        getInitialState:function(){
          return {liked:false};
        },
        handleClick:function(event){
          this.setState({liked: !this.state.liked});

        },
        render:function(){
          var text=this.state.liked?'like':'dont like';
          return(
              <p onClick={this.handleClick}>
              you {text}
          </p>
          );
        }


      });
      ReactDOM.render(
      <LikeButton/>,
          document.getElementById('example')
      );

上面代码是一个LikeButton 组件,它的getInitialState 方法用于定义初始状态 。也就是一个对象,这个对象可以通过this.state 属性读取。当用户点击组件,导致状态变化,this.setState 方法去修改状态值,每次修改以后 自动调用 this.render 方法再次渲染组件。

由于 this.props 和 this.state 都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,this.props 表示那些一旦定义,就不再改变的特性,而 this.state 是会随着用户互动而产生变化的特性。

表单

用户在表单填入的内容,属于用户跟组件的互动,所以不能用 this.props 读取(查看 demo9 )。

var Input = React.createClass({
        getInitialState:function(){
          return {value:"hello"}
        },
        handleChange:function(e){
          this.setState({value:e.target.value})
        },
        render:function(){
          var value=this.state.value;
          return(
              <div>
              <input value={value} onChange={this.handleChange} type="text" />
              <p>{value}</p>
              </div>

          );
        }
      });
      ReactDOM.render(
      <Input />,
          document.getElementById('example')

      );

上面代码中,文本输入框的值,不能用 this.props.value 读取,而要定义一个 onChange 事件的回调函数,通过 event.target.value 读取用户输入的值。textarea 元素、select元素、radio元素都属于这种情况,更多介绍请参考官方文档。