欢迎光临
我们一直在努力

React 的 constructor 与 getInitialState 区别详解:掌握组件状态初始化的演进史

一、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>;
}
});

这种写法有以下几个特点:

  • 不需要手动绑定 this,createClass 会自动绑定。
  • 返回值必须是一个对象,作为初始 state。
  • 与 mixins 配合使用时,多个 mixin 的 getInitialState 会被合并。
  • 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 写法的关键点:

  • 必须先调用 super(props),否则 this 未定义。
  • 通过 this.state 直接赋值,这是 constructor 中唯一可以直接赋值 state 的位置。
  • 事件处理函数需要手动 bind(this)。
  • 二、核心区别对比:理解两者在不同维度的差异

    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,但执行时机在内部机制上略有差异:

  • getInitialState 在组件实例创建后、render 之前调用,由 React 内部触发。
  • constructor 是 JavaScript 原生的构造函数,在实例创建时立即执行,早于任何 React 生命周期。
  • constructor 中可以做一些 state 之外的工作 (如初始化 ref、订阅等),而 getInitialState 只用于返回 state。
  • 三、实战中的选择与最佳实践:在真实项目中正确使用 constructor

    3.1 何时使用 constructor

    在以下场景中仍然需要使用 constructor:

  • 需要在组件挂载前初始化 state,且初始值需要基于 props 计算。
  • 需要为事件处理函数绑定 this。
  • 需要初始化非 state 的实例属性 (如 ref、订阅器)。
  • 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 也正式进入弃用流程。新项目应避免使用,老项目应迁移。以下几个迁移要点需要注意:

  • createClass 中的 mixins 必须用高阶组件或自定义 Hook 替换。
  • 自动 this 绑定必须改为手动 bind 或使用箭头函数。
  • propTypes 和 defaultProps 从组件属性迁移到 static 属性。
  • 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 没有直接等价物,常见替换策略:

  • 状态逻辑用高阶组件 (HOC) 包装。
  • 通用工具函数提取为独立模块。
  • 生命周期相关逻辑用 Hooks (若已升级到 16.8+)。
  • 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 团队对简洁、可组合、可预测的不懈追求。

    赞(0)
    未经允许不得转载:171主机测评 » React 的 constructor 与 getInitialState 区别详解:掌握组件状态初始化的演进史
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址