React中的数据承载-Props/State
任意的视图变化都应该由数据来控制
React也是基于数据驱动(声明式)的框架,组件中必然需要承载一些数据,在react中起到这个作用的是属性和状态(props & state)
-
属性(props) 在组件外部传入,或者内部设置,组件内部通过this.props获得
-
状态(state) 在组件内部设置或者更改,组件内部通过this.state获得
属性(props)
属性一般是外部传入的,组件内部也可以通过一些方式来初始化的设置,属性不能被组件自己更改
属性是描述性质、特点的,组件自己不能随意更改
使组件拥有属性的方式:
- 在装载(mount)组件的时候给组件传入
传入数据的时候,除了字符串类型,其他的都应该包上表达式,但是为了规整,所有的数据传递,最好都包上{}
var Gouzi = React.createClass({
render(){
console.log(this)
return (
<div>
<p>我的名字:{this.props.name}</p>
<p>我的性别:{this.props.sex}</p>
<p>我的年龄:{this.props.age}</p>
<p>我的父亲是:{this.props.father}</p>
</div>
)
}
})
let info = {
sex:'male',
father:'狗爸'
}
ReactDOM.render(<Gouzi {...info} name={"大狗子"} age={26}/>,app)
- 父组件给子组件传入
父组件在嵌套子组件的时候为子组件传入,传入的方式和上面的方式一样
//父组件的render函数
render(){
return (
<div>
<p>父组件:</p>
<hr/>
<Son name={'大狗子'}/>
<Son name={'二狗子'}/>
</div>
)
}
- 根据属性或状态,我们可以在render中的表达式里做一些逻辑判断,可以使用||、三元表达式、子执行函数等等
getName(){
return this.props.name || '野狗子'
},
render:function () {
let {name} = this.props
return (
<div>
<p>我是子组件-{this.props.name || '野狗子'}</p>
<p>我是子组件-{this.props.name?this.props.name:'野狗子'}</p>
<p>我是子组件-{this.getName()}</p>
</div>
)
}
状态(state)
状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同的状态下使组件的显示不同(自己管理)
在组件中可以通过constructor构造函数来给组件挂载初始状态,在组件内部通过this.state获取
this.props和this.state是纯js对象,在vue中, data的数据的时候会触发数据的getter和setter,但是react中没有做这样的处理,如果直接更改的话,react是无法得知的,所以,需要使用特殊的更改状态的方法:
setState(params)
在setState中传入一个对象,就会将组件的状态中键值对的部分更改,还可以传入一个函数,这个回调函数必须返回像上面方式一样的一个对象,函数可以接收prevState和props
//1.
let doing = this.state.doing=='学习'?'玩游戏':'学习'
this.setState({doing})
//2.
this.setState((prevState,props)=>{
return {
doing:prevState.doing=='学习'?'玩游戏':'学习'
}
})
setState
有两个参数
第一个参数可以是对象,也可以是方法return一个对象,我们把这个参数叫做updater
- 参数是对象
let dos = this.state.doing==="学英语"?"玩游戏":"学英语";
this.setState({
doing:dos
});
-
参数是方法
this.setState((prevSate,props)=>{ return { doing:prevSate.doing==="学英语"?"玩游戏":"学英语" } });
注意的是这个方法接收两个参数,第一个是上一次的state, 第二个是props
setState
是异步的,所以想要获取到最新的state,没有办法获取,就有了第二个参数,这是一个可选的回调函数
this.setState((prevSate,props)=>{
return {
doing:prevSate.doing==="学英语"?"玩游戏":"学英语"
}
}, () => {
console.log('回调里的',this.state.doing) //获取更新后的值
})
console.log('setState外部的',this.state.doing) //还是之前没有更新的值
两种setState的比较:
另外setState(stateChange[, callback])这种形式的更改状态,会将传入的对象浅层合并到新的 state 中,例如,调整购物车商品数:
this.setState({quantity: 2})
这种形式的 setState()
是异步的,并且在同一周期内会对多个 setState
进行批处理。例如,如果在同一周期内多次设置商品数量增加,则相当于:
Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
后调用的 setState()
将覆盖同一周期内先调用 setState
的值,因此商品数仅增加一次。如果后续状态取决于当前状态,我们建议使用 updater 函数的形式代替:
this.setState((prevState,props) => {
return {quantity: prevState.quantity + 1}; //连续执行多次获放入队列中一次执行
}[, callback]);
扩充:super的理解
es5里面的构造函数与继承实现:
/* function Person(name,age){
this.name = name;
this.age = age
}
Person.prototype.say = function(){
return this.name+","+this.age
}
let p = new Person("张三",18)
let p2 = new Person("李四",18)
// console.log(p.say === p2.say)
console.log(p.say()) */
//es5中的继承
function Person(name,age){
this.name = name;
this.age = age
}
Person.prototype.say = function(){
return this.name+","+this.age
}
function Student(name,age,school){
Person.call(this,name,age) //借用构造函数实现继承
this.school = school
}
/* Student.prototype = Person.prototype;
Student.prototype.marray = true
let stu = new Student("张三",18,"千锋教育")
let per = new Person("张四",38)
console.log(per.marray) */
Student.prototype = Object.create(Person.prototype); //实现了原型链继承
Student.prototype.constructor = Student //扭转Student.prototype的constuctor属性为Student
es6的类与继承与super理解:
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
return this.name+","+this.age
}
}
//es6中constuctor构造函数里面为啥调用super?
//说白了,就是让父类的构造函数执行一遍来去给子类赋值属性
class Student extends Person{
constructor(name,age,school){
super(name,age) //es6中super必须放在构造函数第一行
this.school = school
}
say(){
return super.say()+","+this.school
}
}
let per = new Person("张三",38)
let stu = new Student("张四",18,"千锋教育")
console.log(stu.say())
实现下拉菜单的方式
- 通过数据来控制元素的行内样式中display的值,或者去控制类名
<ul style={{display:isMenuShow?'block':'none'}}><li>国内新闻</li></ul>
...
<ul className={isMenuShow?'show':'hide'}><li>国内新闻</li></ul>
- 根据数据控制是否渲染改节点、组件
{
isMenuShow?<ul><li>国内新闻</li></ul>:''
}
- 通过ref对dom、组件进行标记,在组件内部通过this.refs获取到之后,进行操作
<ul ref='content'><li>国内新闻</li></ul>
...
this.refs.content.style.display = this.state.isMenuShow?'block':'none'
属性和状态的对比
相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
不同点:
- 属性能从父组件获取,状态不能
- 属性可以由父组件修改,状态不能
- 属性能在内部设置默认值,状态也可以
- 属性不在组件内部修改,状态要改
- 属性能设置子组件初始值,状态不可以
- 属性可以修改子组件的值,状态不可以
state
的主要作用是用于组件保存、控制、修改自己的可变状态。state
在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state
是一个局部的、只能被组件自身控制的数据源。state
中状态可以通过 this.setState
方法进行更新,setState
会导致组件的重新渲染。
props
的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props
,否则组件的 props
永远保持不变。
没有 state
的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多写无状态组件,尽量少写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。