react学习笔记

1. React.createClass是什么?

学习react最先接触的应该就是createClass了,一般我们这么写,

1
2
3
4
5
   var Li=React.createClass({
render:function(){
return (<li className="li">test</li>)
}
});

return后面使用jsx语法,可以直接返回html元素,(注意两点,class要改成classNamefor改为htmlFor
首先注意组件名首写字母要大写,其次,每一个组件必须要有一个render的属性,否则报错。这个时候我们把Li打印一下,看看是什么鬼,发现它是一个function,我们把这个组建放入到页面,然后用一个变量接收

1
var ss=ReactDOM.render(<Li />, document.querySelector('#app'));

这时候,ss又是什么鬼呢?打印一下,发现他是一个object,其实就是组件的一个实例,如果给Li加一些方法,在ss对象里还可以看到,也就是说可以在外面使用这些方法了,比如设置值,获取值。

2. 表单

页面不可避免要使用表单,在react里表单稍微有点不一样,

1
2
3
4
5
    var Input=React.createClass({
render:function(){
return (<input type="text" defaultValue="aaa"/>)
}
});

这里只能使用defaultValue,直接使用value你会发现这个表单无法输入了,控制台也会提醒你

You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly. Check the render method of Li.

也就是说,你要么使用defaultValue,或者加一个 onChange事件,或 readOnly属性表明它只读不会变。

3. this.statethis.props

组建写好就是给人使用的,所以你不能把东西写死,要按照别人使用时传给你什么就显示什么,把上面的例子改造一下

1
2
3
4
5
6
7
8
9
   var Input=React.createClass({
_change:function(e){
console.log(e.target.value);
},
render:function(){
return (<input onChange={this._change} type="text" defaultValue={this.props.data}/>)
}
});
ReactDOM.render(<Input data="hello"/>, document.querySelector('#app'));

使用组件时,加一个data属性,在组件里面就可以使用this.props.data获取
注意三点:

  1. jsx里面要是有变量必须放在{}里面。
  2. 组件自己内部的方法,可以加一个_,如果想暴露在外面就不加,这样方便其他同事辨认。
  3. 组件自己内部的方法,在每个实例都是同样的一份,不像函数原型共用一份。

但是,如果此时我想获取到用户输入的值,怎么办?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
     var Input=React.createClass({
getInitialState:function(){
//这个函数只会在初始化时执行一次,以后更新数据不会执行
return ({data:this.props.data?this.props.data:""});
},
_change:function(e){
this.setState({data:e.target.value});
},
render:function(){
//每次更新都会执行一次render
return (<div><input onChange={this._change} type="text" value={this.state.data}/><span>{this.state.data}</span></div>);//这里必须要有一个大的标签包着
}
});
ReactDOM.render(<Input data="hello"/>, document.querySelector('#app'));

在这里,getInitialState设置初始化的state值,如果设置了属性,则显示属性,否则为空,每次改变input里的值后,this.setState会重新设置新的状态,把用户输入的值显示到span标签里。
两者比较来说,this.props是由组件使用者设置的,是不会改变的,除非组件使用者重新设置了这个值(如果使用反模式,把它放在getInitialState里,因为只会在初始化时执行一次,父组件更新数据不会执行,所以这种情况下,虽然this.props改变了,但this.state没变,页面显示就不会更新了,如果保证以后父组件不会改变这个数据,可以这样使用,但如果不能保证就不能这样用了),而this.state是组件内部的一个状态,是会经常改变的。
如果在父组件想改变子组件的数据,一种方法是子组建那个数据使用this.props,父组建改变属性值,但如果组件内部也想改变这个数据,那就不行了。另一种方法是子组件使用this.state,但暴露一个方法比如set,接收一个参数,里面用this.setState去改变值,父组件拿到子组件实例的引用,调用set改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   var Input=React.createClass({
getInitialState:function(){
return ({data:this.props.data?this.props.data:""});
},
_change:function(e){
this.setValue(e.target.value);
},
setValue:function(d){
this.setState({data:d});
},
render:function(){
return (<div><input onChange={this._change} type="text" value={this.state.data}/><span>{this.state.data}</span></div>);
}
});
var Page=React.createClass({
_click:function(){
this.refs.input.setValue("888");
},
render:function(){
return (<div><Input ref="input" data="www"/><button onClick={this._click}>click me</button></div>)
}
});
ReactDOM.render(<Page/>, document.querySelector('#app'));

4. ref是什么?

  1. 如果在html里设置ref那么它就指向这个真实的DOM节点。
  2. 如果在组件里设置ref,那么它就指向这个组件实例的引用,和组件里面的this互等。

我们经常在表单input,select里使用,获取其value,this.refs.ad.value

5. key有什么用?

一个组件,可能会调用很多次,比如在ul里有很多个li,一般我们使用map方法给li循环加上数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   var Li=React.createClass({
getInitialState:function(){
return ({data:this.props.data?this.props.data:""});
},
render:function(){
return (<li className="li">{this.state.data}</li>)
}
});
var data=[{name:'name1',id:1},{name:'name2',id:2},{name:'name3',id:3},{name:'name4',id:4}];
var List=React.createClass({
getInitialState:function(){
return ({data:data});
},
render:function(){
return(<div><ul>
{this.state.data.map(function(item,index){
console.log(item);
return(<Li key={item.id} data={item.name} />)
})}
</ul></div>)
}
})

这个时候有很多个Li,为了区分各个Li实例,方便以后如果数据修改了可以快速更新,给每个Li设置一个唯一的key,比如把id为3的数据删了,然后重新渲染Listdiff把现在的数据和上一次的数据对比,发现少了第三条数据,直接在页面把第三条数据删除,其他的都不用变。如果不设置唯一的key,那么久没办法区分了。

6. componentWillReceiveProps

componentWillReceiveProps在将要接受新的props时被调用,虽然说一般props是不可变状态,但情况通常是这样的,当一个父组件包含了一个子组件,子组件的一个props的值是父组件的states的值,那么当父组件可变状态改变时,子组件的props也更新了,于是调用了这个函数。

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
   var Li=React.createClass({
getInitialState:function(){
return ({data:this.props.data?this.props.data:"1"})
},
componentWillReceiveProps :function(nextProps){
console.log(nextProps,"nextProps");
if(nextProps.data!=this.state.data){
this.setState(nextProps,function(){
console.log(this.state,"state2");
});
console.log(this.state,"state");
}
},
render:function(){
console.log(this.state。data,"render");
return (<li className="li">{this.state.data}</li>)
}
});
var data=[{name:'name1',id:1},{name:'name2',id:2},{name:'name3',id:3},{name:'name4',id:4}];
var List=React.createClass({
getInitialState:function(){
return ({data:data});
},
add:function(){
this.state.data[0].name="aaa";
this.setState({data:data});
},
render:function(){
return(<div><ul>
{this.state.data.map(function(item,index){
console.log(item);
return(<Li key={item.id} data={item.name} />)
})}
</ul></div>)
}
})

调用add方法,将第一个数据的name改变,由于这个时候Li组件已经初始化完成,不会再调用getInitialState了,render里面的{this.state.data}自然也不会改变,所以此时设置componentWillReceiveProps,比较上一个this.state与新传入的props是不是一样,改变了就调用this.setState设置新值,这也是唯一可以在组件更新周期中调用this.setState的函数。
这种情况只适合于子组件的props反模式为state,在组件内部可能会改变值,如果不会改变的话,在子组件render里直接用props就完事了。

7. shouldComponentUpdate

在上面的例子我们可以看到,我只是改变第一个数据的name,但从控制台打印信息可以看到,render仍然执行了四次,虽然其他三次都是没必要的。如果是几百个组件,就有点浪费性能了,所以我们在<Li>组件里加入shouldComponentUpdate,比较属性是否改变,如果返回true则更新,false则不更新,这样来优化性能。

1
2
3
4
   shouldComponentUpdate:function(nextProps,nextState){
console.log(nextProps,nextState,"should")
return (nextProps.data!=this.state.data)
},

现在,render只执行了一次。如果add方法里还加一句代码,新增一条数据,这时新增的数据不会执行componentWillReceivePropsshouldComponentUpdate,而是执行了getInitialState,这又是key的好处了。

坚持原创技术分享,您的支持将鼓励我继续创作!