一、React 组件状态初始化的演进:从 getInitialState 到 constructor
1.1 React 状态初始化的历史背景
React 自 2013 年开源以来,组件写法经历了多次重大变革。早期 React 0.13 版本之前,组件创建主要依赖 React.createClass 方法,那时的状态初始化必须通过 getInitialState 钩子完成。2015 年 React 0.13 版本正式引入 ES6 class 写法后,constructor 逐渐成为状态初始化的标准位置。理解两者的区别,本质上是理解 React 组件 API 演进史。
1.2 getInitialState 的本质:ES5 时代的产物
getInitialState 是 React.createClass 工厂函数提供的一个生命周期钩子,它会在组件挂载前被调用,返回值即为组件的初始 state。其典型用法如下:
const Counter = React.createClass({
getInitialState() {
return { count: 0 };
},
render() {
return <div>{this.state.count}</div>;
}
});
这种写法有以下几个特点:
1.3 constructor 的崛起:ES6 class 时代的标准
随着 ES6 class 语法的普及,React 推出了 extends React.Component 的写法。此时状态初始化被移到了 constructor 中,通过 this.state = … 直接赋值:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return <div>{this.state.count}</div>;
}
}
constructor 写法的关键点:
二、核心区别对比:理解两者在不同维度的差异
2.1 语法层面的差异
| 维度 | getInitialState | constructor |
| — | — | — |
| 所属 API | React.createClass | ES6 class extends React.Component |
| 调用方式 | 钩子函数,返回对象 | 构造函数,直接赋值 this.state |
| this 绑定 | 自动绑定 | 需手动绑定 |
| mixins 支持 | 支持 | 不支持 |
| 当前状态 | 已废弃 | 仍可用但不再推荐 |
2.2 this 绑定行为的差异
这是开发者最容易踩坑的地方。在 createClass 写法中,React 会自动为每个方法绑定 this 到组件实例:
const Button = React.createClass({
handleClick() {
console.log(this); // 自动绑定到组件实例
},
render() {
return <button onClick={this.handleClick}>click</button>;
}
});
而在 ES6 class 写法中,方法默认不会绑定 this,必须在 constructor 中显式绑定:
class Button extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this);
}
render() {
return <button onClick={this.handleClick}>click</button>;
}
}
2.3 执行时机与生命周期的差异
两者虽然都用于初始化 state,但执行时机在内部机制上略有差异:
三、实战中的选择与最佳实践:在真实项目中正确使用 constructor
3.1 何时使用 constructor
在以下场景中仍然需要使用 constructor:
class SearchInput extends React.Component {
constructor(props) {
super(props);
this.state = {
keyword: props.initialKeyword || '',
};
this.inputRef = React.createRef();
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
render() {
return <input ref={this.inputRef} value={this.state.keyword} onChange={this.handleChange} />;
}
}
3.2 getInitialState 的弃用场景
自 React 15.5 版本起,React.createClass 被抽取到独立的 create-react-class 包中,getInitialState 也正式进入弃用流程。新项目应避免使用,老项目应迁移。以下几个迁移要点需要注意:
3.3 现代写法:直接赋值 state 的简写形式
如果不需要在 constructor 中执行额外逻辑,可以使用类属性语法直接初始化 state:
class Counter extends React.Component {
state = { count: 0 };
handleClick = () => {
this.setState(state => ({ count: state.count + 1 }));
};
render() {
return <button onClick={this.handleClick}>{this.state.count}</button>;
}
}
这种写法配合箭头函数,既避免了手动 bind,又让代码更加简洁,是目前 class 组件的推荐写法。
3.4 Hooks 时代的全新思路
React 16.8 引入 Hooks 后,函数组件也能拥有 state,useState 成为主流方案:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
在 Hooks 时代,constructor 和 getInitialState 都已成为历史,新代码应优先采用函数组件 + Hooks 的组合。
四、流程图解:constructor 与 getInitialState 执行时机对比
下面的 mermaid 流程图直观展示了两种写法在组件初始化阶段的执行位置:
#publish-mermaid-1785949061451-0{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1785949061451-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1785949061451-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1785949061451-0 .error-icon{fill:#552222;}#publish-mermaid-1785949061451-0 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1785949061451-0 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1785949061451-0 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1785949061451-0 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1785949061451-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1785949061451-0 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1785949061451-0 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1785949061451-0 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1785949061451-0 .marker.cross{stroke:#333333;}#publish-mermaid-1785949061451-0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-1785949061451-0 p{margin:0;}#publish-mermaid-1785949061451-0 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-1785949061451-0 .cluster-label text{fill:#333;}#publish-mermaid-1785949061451-0 .cluster-label span{color:#333;}#publish-mermaid-1785949061451-0 .cluster-label span p{background-color:transparent;}#publish-mermaid-1785949061451-0 .label text,#publish-mermaid-1785949061451-0 span{fill:#333;color:#333;}#publish-mermaid-1785949061451-0 .node rect,#publish-mermaid-1785949061451-0 .node circle,#publish-mermaid-1785949061451-0 .node ellipse,#publish-mermaid-1785949061451-0 .node polygon,#publish-mermaid-1785949061451-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785949061451-0 .rough-node .label text,#publish-mermaid-1785949061451-0 .node .label text,#publish-mermaid-1785949061451-0 .image-shape .label,#publish-mermaid-1785949061451-0 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1785949061451-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1785949061451-0 .rough-node .label,#publish-mermaid-1785949061451-0 .node .label,#publish-mermaid-1785949061451-0 .image-shape .label,#publish-mermaid-1785949061451-0 .icon-shape .label{text-align:center;}#publish-mermaid-1785949061451-0 .node.clickable{cursor:pointer;}#publish-mermaid-1785949061451-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1785949061451-0 .arrowheadPath{fill:#333333;}#publish-mermaid-1785949061451-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1785949061451-0 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1785949061451-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785949061451-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1785949061451-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785949061451-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1785949061451-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1785949061451-0 .cluster text{fill:#333;}#publish-mermaid-1785949061451-0 .cluster span{color:#333;}#publish-mermaid-1785949061451-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1785949061451-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1785949061451-0 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1785949061451-0 .icon-shape,#publish-mermaid-1785949061451-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785949061451-0 .icon-shape p,#publish-mermaid-1785949061451-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1785949061451-0 .icon-shape .label rect,#publish-mermaid-1785949061451-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785949061451-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1785949061451-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1785949061451-0 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].node rect,#publish-mermaid-1785949061451-0 [data-look=\”neo\”].cluster rect,#publish-mermaid-1785949061451-0 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785949061451-0 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785949061451-0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}createClass 写法ES6 class 写法
组件创建请求
组件类型判断
调用 getInitialState
合并 mixins 的 state
自动绑定方法 this
执行 render
执行 constructor
调用 super props
赋值 this.state
手动 bind 事件方法
componentDidMount
从图中可以看出,两条路径最终都会汇入 render,但 createClass 路径多了 mixins 合并与自动绑定步骤,而 class 路径则需要开发者自行处理 this 绑定。
五、迁移指南:从 getInitialState 到 constructor
5.1 第一步:替换组件声明方式
将 createClass 改为 class 继承:
// before
const MyComponent = React.createClass({ … });
// after
class MyComponent extends React.Component { … }
5.2 第二步:迁移 getInitialState 到 constructor
// before
getInitialState() {
return { list: [] };
}
// after
constructor(props) {
super(props);
this.state = { list: [] };
}
5.3 第三步:处理 mixins
mixins 没有直接等价物,常见替换策略:
5.4 第四步:补充 this 绑定
将自动绑定改为显式绑定:
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleReset = this.handleReset.bind(this);
}
5.5 第五步:迁移静态属性
propTypes 和 defaultProps 改为 static 写法:
// before
const MyComponent = React.createClass({ … });
MyComponent.propTypes = { title: PropTypes.string };
MyComponent.defaultProps = { title: 'hello' };
// after
class MyComponent extends React.Component {
static propTypes = { title: PropTypes.string };
static defaultProps = { title: 'hello' };
}
掌握 constructor 与 getInitialState 的区别,不仅是应付面试的高频问题,更是理解 React 设计哲学演进的一把钥匙。从 createClass 到 class,再到 Hooks,每一次变化都折射出 React 团队对简洁、可组合、可预测的不懈追求。
