React的组件可以分为函数组件和类组件。函数组件样式简单,但是不能拥有自己的内部状态。相反的,类组件写起来比较复杂,但是功能却比较强大。
函数组件
直接上代码:
function App() {
function add([x, y]) {
return x + y
}
return (
<div>
<Box name='leo' add1={add}></Box>
</div>
)
}
function Box(props) {//
return (
<div>
{props.name}
{props.add1([1, 5])}
</div>
)
}
上面代码就是两个简单的父子函数组件,子组件要获取父组件的内容,可以通过传参的方法,props就是参数。
类组件
先把上面的两个父子组件改为class的型式:
class App extends React.Component {
add([x, y]) {
return x + y
}
render() {
return (
<div>
<Box name='leo' add1={this.add.bind(this)}></Box>
</div>
)
}
}
class Box extends React.Component {
render() {
console.log(this)
console.log(this.props)
return (
<div>
{this.props.name}
{this.props.add1([1, 5])}
</div>
)
}
}
这段代码难以理解的有:
- 每个组件中的this是什么
- 子组件中:
this.props
是什么 - 父组件的中:
add1={this.add.bind(this)
为什么要.bind(this)
解答
- 在class中,this是指组件本身
- this.props 是一个对象,里面存储着this的属性。
比如在子组件Box中,this.props
就是存储Box的属性的对象,打出来就是
Box{props :name : 'leo' ,add1: f() } - 这一个问题比较复杂,简单的说,就是绑定add()这个函数的this为原组件,在上面那个例子中就是App。需要用另一例子才能解释:
我们还是写两个类组件,父组件是App,子组件是Box。子组件的props对象里面有两个属性,一个属性名为add1,属性值为App.add,是一个函数;一个属性名为age,属性值为App.state.age。子组件的constructor()会调用父组件App的add()函数,让App.state.age的值改变。正确的代码为:
//父组件
class App extends React.Component {
constructor() {
super()
this.state = {
age: 0
}
}
add() {
setTimeout(() => {
this.setState({
age: 2
})
}, 2000)
}
render() {
return (
<div>
<Box add1={this.add.bind(this)} age={this.state.age} ></Box>
</div>
)
}
}
//子组件
class Box extends React.Component {
constructor(props) {
super()
let n = 1
if (n === 1) {
props.add1()
n += 1
}
}
render() {
return (
<div>
{this.props.age}
</div>
)
}
}
现在可以开始解释为什么要绑定this了:
- 子组件的
props.add1()
这个代码,会找到props也就是存储Box的属性的对象的地址,然后在里面找到add1属性,add1的属性值是一个函数,为App.add()
。所以会引用那个函数对象的地址,因此props.add1()的地址和App.add()的地址是一样的。 - 问题在于:
App.add()
这个函数里面调用了this.setState
这个函数。在App.add()
中,this就是指App,但是在被props.add1()
引用的时候,this就变成了props(具体就是那个存在Box属性的对象)。 - 由于Box.props里面并没有setState()这个函数,所以必须在写add1属性的时候,就把add()的this绑定为App。
- .bind(this)就是这个目的。
如果不想写bind有没有办法呢?
有的,写bind主要是因为this会随着环境而变化,很麻烦。this就是js的糟粕,在es6出现的箭头函数,this不会再随着环境的变化而变化了,而是直接作为一个参数直接确定下来。也就是说,如果在class App里面申明一个箭头函数,箭头函数语句中出现的this,都是指App,所以就可以不用bind了。