React手动实例化组件调用render与直接挂载组件的差异及报错原因
写法2(JSX写法)的运行逻辑
ReactDOM.render(<MyComponentButton/>, document.getElementById('state_change_component_jsx'))
这种写法里的<MyComponentButton/>是JSX语法,会被Babel转译为React.createElement(MyComponentButton, null)调用,返回的是React元素——这是一个描述组件渲染信息的普通对象,而非组件实例。
你把这个React元素传给ReactDOM.render后,React会自动执行完整的组件挂载流程:
- 内部创建
MyComponentButton的类实例 - 给实例挂载React内部所需的调度属性、挂载状态标记
- 执行组件挂载生命周期钩子,将组件和真实DOM节点绑定
此时组件处于已挂载状态,调用setState时,React的内部调度器会正常处理状态更新、触发重渲染。
写法1(手动new实例写法)的问题
let stateChange = new MyComponentButton(); ReactDOM.render(stateChange.render(), document.getElementById('state_change_component_jsx'));
这种写法的问题核心是:你手动创建的组件实例完全脱离了React的管理体系,React根本感知不到这个实例的存在:
- 你手动调用
stateChange.render()得到的只是组件返回的静态UI元素树(div、按钮、标题组成的结构),ReactDOM.render最终渲染的是这堆和stateChange实例没有任何绑定关系的UI,React不会把这部分UI和你手动创建的实例做关联 - 手动创建的实例没有被React标记为已挂载,也没有绑定React内部的更新调度器。当你点击按钮触发
handleClick时,调用的是这个游离实例的setState,React检测到该实例未进入挂载流程,就会抛出你遇到的警告:
Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to
this.statedirectly or define astate = {};class property with the desired state in the MyComponentButton component.
哪怕你忽略警告,状态更新也不会触发UI重渲染,因为React不会监听这个游离实例的状态变化。
核心结论
手动new组件实例不等于完成组件挂载,组件挂载的完整流程必须由React触发,所有组件实例的管理、状态更新调度都由React内部统一处理,开发者只需要通过JSX给React传递组件描述即可,不需要手动创建组件实例。
内容的提问来源于stack exchange,提问作者ericcheong_

