在 React 中初始化 State 的位置
啊,初始化状态的各种方法。这可能令人困惑。是将 state = {...}
直接放在类里面,还是编写一个构造函数并在构造函数里面写 this.state = { ... }
?并且,你是否需要完全使用构造函数?
啊,初始化状态的各种方法……这可能令人困惑。是将 state = {...}
直接放在类里面,还是编写一个构造函数并在构造函数里面写 this.state = { ... }
?并且,你是否需要完全使用构造函数?
初始化状态的两种方法
在 React 组件中初始化状态有两种方法:在构造函数内部,以及直接在类内部。这里有一些例子。
在构造函数内部
在构造函数内部初始化状态如下所示
class App extends React.Component { constructor(props) { // Required step: always call the parent class' constructor super(props); // Set the state directly. Use props if necessary. this.state = { loggedIn: false, currentState: "not-panic" someDefaultThing: this.props.whatever } } render() { // whatever you like } }
当组件类被创建时,构造函数是第一个被调用的方法,所以它是初始化所有东西的正确位置 - 包括状态。类实例已经在内存中创建,因此您可以使用 this
在其上设置属性。
这是唯一一个可以在等号左侧使用 this.state
的地方。在其他任何地方,都应该始终使用 this.setState
而不是使用 this.state.whatever = ...
- 这样,React 就会知道您更改了某些内容,并且它可以重新渲染组件。
在编写构造函数时要注意的一件事是确保调用父类的构造函数:上面示例中的 super(props)
行。默认构造函数(由 JS 在你创建类时提供)会自动调用 super
并传入任何参数。
通过编写您自己的构造函数,您正在覆盖该默认行为,除非您自己调用 super
,否则如果父类需要进行一些初始化,这可能会导致错误。
是否需要构造函数?
您不需要编写一个,因为 JS 提供了一个默认构造函数。要了解这是如何工作的,请尝试在浏览器的控制台中运行这 3 行
class Parent { constructor(arg) { console.log('constructing Parent with', arg) } } class Child extends Parent {} new Child(5);
请注意,当您创建一个新的 Child 时,它会打印“constructing Parent with 5”,即使 Child 没有明确定义的构造函数,并且没有明确地使用 super(arg)
调用父类的构造函数。 当您没有定义自己的构造函数时,JS 会自动处理这个 super
调用。
直接在类内部
初始化状态的第二种方法是直接在类定义内部,使用类属性。这看起来像这样
class App extends React.Component { state = { loggedIn: false, currentState: "not-panic", someDefaultThing: this.props.whatever } render() { // whatever you like } }
简洁明了!这里有几点需要注意
- 没有构造函数
- 直接引用
state
属性。它不是this.state
,只是state
。 - 范围在类内部,但不在方法内部。
- 您仍然可以引用
this.props
(和this.context
)。 - 这是一个类 实例 属性,而不是静态属性,您可能将其用于 propTypes(例如
static propTypes = {...}
)。
在我写这篇文章时,类属性语法是一个 Stage 3 proposal,所以它还没有成为官方的 JS 规范的一部分。要使用它,您需要启用 Babel 的类属性转换。
但是!如果您使用 Create React App 来引导您的项目,它已经启用了类属性转换,并且您今天就可以开始使用这个类属性语法了。
哪个更好?构造函数还是不使用?
和所有事情一样,这取决于您。
我更喜欢类属性的简洁外观。我不喜欢构造函数的额外样板,并且必须记住调用 super(props)
(尽管 ESlint 可以提醒你这样做,而且 Create React App 的配置开箱即用)。
您可能已经看到在构造函数中绑定了事件处理函数,并且可能认为构造函数是必需的。我正在谈论这样的代码
class Thing extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick(event) { // do stuff } }
类属性功能支持的另一种语法可以使这个构造函数不必要:您可以将属性设置为等于箭头函数,并且箭头函数继承类实例的 this
绑定,因此您不必显式地绑定它。它看起来像这样
class Thing extends React.Component { // This is all you need to do: handleClick = (event) => { // do stuff } }
这起初看起来可能有点奇怪,但您可以这样想
// This statement: const add = (a, b) => console.log(a + b); // Can be thought of as assigning an arrow function to `add`: const add = arrowFunction; // where `arrowFunction` is expanded to: (a, b) => console.log(a + b)
考虑到这一点,再看一下上面的 class Thing
示例。希望它看起来不那么奇怪。如果您仍然讨厌它,请花一些时间并编写更多箭头函数。我最初也有同样的问题。你的眼睛会适应的 :)
在 React 中初始化状态的位置最初由 Dave Ceddia 于 2018 年 3 月 29 日在 Dave Ceddia 上发布。