React基础11 React中初始化state的两种方法

在React的组件中可以在两个位置来初始化state:(1)在组件的constructor中;(2)直接在class中利用属性赋值的方式

constructor

constructor中初始化state如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class App extends React.Component {
constructor(props) {
// 必须在这里通过super调用父类的constructor
super(props);

// 给state赋值,可以使用props
this.state = {
loggedIn: false,
currentState: "not-panic",

// 在使用props为state赋值时要格外仔细
someInitialValue: this.props.initialValue
}
}

render() {
// whatever you like
}
}

当一个class组件创建之后,constructor会首先被调用,所以在construcot中可以来初始化所有值,包括state。class实例在内存中已经被创建,所以可以使用this来为state赋值

要注意的是,在constructor中可以不使用this.setState来为state直接赋值,除此之外其他位置都不能这样做:

1
2
// 除了在constructor中,其他位置都不要这样做
this.state.a = 123

只有通过setState才能通知React,我们修改了数据,React需要重新渲染组件。

还有要注意的是,在constructor中不要忘记使用super(props)来调用父类的constructor。默认的constructor(当创建一个class时,如果我们没有显式的声明constructor,JS会默认提供一个)会自动调用super,将将所有的参数传入

使用prop来初始化state

大多数情况下都不要使用prop来为State的初始化赋值,因为这会让你的数据来源不唯一,这常常会导致Bug。数据源唯一是最佳实践。

当组件的prop发生改变,组件会重渲染,所以没有必要将prop复制为state来保证porp永远是最新的值。

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
// 不要这样做
class BadExample extends Component {
state = {
data: props.data
}

componentDidUpdate(oldProps) {
// 复制了prop的值给state之后
// 必须保证在props更新时,state的值也随之更新
if(oldProps.data !== this.props.data) {
// This triggers an unnecessary re-render
this.setState({
data: this.props.data
});
}
}

render() {
return (
<div>
The data: {this.state.data}
</div>
)
}
}

// 正确的做法:
class GoodExample extends Component {
render() {
return (
<div>
The data: {this.props.data}
</div>
)
}
}

constructor是必须的吗?

并一定必须显式的定义constructor,因为JS会提供默认的constructor。比如下面这个例子:

1
2
3
4
5
6
7
8
9
10
class Parent { 
constructor(arg) {
console.log('constructing Parent with', arg)
}
}

class Child extends Parent {}

new Child(5);
// constructing Parent with 5

当创建Child的实例时,控制台会打印出constructing Parent with 5,虽然Child类并没有显式的定义constructor,也没有显式的通过super(props)调用父类。当我们没有定义自己的constructor时,JS会自动完成super的步骤。

直接在Class中定义

第二种初始化state的方法就是直接在Class的内部,使用Class的属性来定义:

1
2
3
4
5
6
7
8
9
10
11
class App extends React.Component {
state = {
loggedIn: false,
currentState: "not-panic",
someDefaultThing: this.props.whatever
}

render() {
// whatever you like
}
}

比如使用construcot更加得直接、简介。要注意的是:

  • 没有定义constructor
  • state属性是直接引用的,并不是通过this.state来引用的
  • state的作用域是在Class内部,并不是一个方法的内部
  • 仍然可以使用this.propsthis.context
  • state是class的实例属性,并不是静态属性,不需要添加static关键字(就像为static propTypes {...}

哪种更好

习惯使用哪种,就使用哪种。

class属性的方式看起来更加简单,不再需要额外的模板代码(constructor),也不需要提醒自己调用super(props)

有的时候需要在constructor中处理事件处理函数(为函数绑定this,就像:

1
2
3
4
5
6
7
8
9
10
class Thing extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}

handleClick(event) {
// do stuff
}
}

可以通过Class属性的另外一种形式来替代上面的形式,可以让一个属性等于一个箭头函数,箭头函数获得了class实例的this,所以不再需要在constructor中显式的去绑定:

1
2
3
4
5
class Thing extends React.Component {
handleClick = (event) => {
// do stuff
}
}

参考