You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React createPortal未插入子元素到容器且不触发UI渲染问题

问题根因

你对ReactDOM.createPortal的用法存在本质错误,这也是点击后没有内容渲染的核心原因:

  • createPortal本身只返回一个特殊格式的React元素,它不会主动执行DOM挂载操作。你在add事件回调里单独调用这个API,生成的React元素根本没有进入React的渲染流程,和声明了一个变量没使用是一个效果,自然不会在页面上产出任何DOM。
  • 你的实现思路是命令式操作DOM,完全不符合React状态驱动渲染的核心逻辑。createPortal从来不是用来替代appendChild做动态节点插入的命令式API。
  • 额外提一句:你的待办场景里li本身就是ul的子节点,根本不需要用到portal,这个API的设计目标是把组件渲染到当前父组件DOM树之外的节点上(比如全局弹窗、悬浮提示这类需要脱离父容器overflow、z-index限制的场景)。
正确实现

标准React写法(不需要portal)

所有UI数据存在组件state中,点击按钮时只需要更新state,React会自动触发重渲染、更新DOM,不要手动操作DOM插入节点:

import React  from 'react'
class App extends React.Component {
  // 初始化待办列表状态
  state = {
    todoList: []
  }
  // 用箭头函数绑定this,避免回调里this指向异常
  add = () => {
    const inputEl = document.getElementById("textInput");
    const textVal = inputEl.value.trim();
    if (!textVal) return;
    // 更新状态触发重渲染
    this.setState({
      todoList: [...this.state.todoList, textVal]
    })
    // 清空输入框
    inputEl.value = '';
  }
  render(){
    return (
      <div>
        To Do list:
        <ul id='ul'>
          {/* 遍历状态渲染所有li节点 */}
          {this.state.todoList.map((todo, idx) => <li key={idx}>{todo}</li>)}
        </ul>
        <input type="text" id="textInput" />
        <input type="button" value="Add" onClick={this.add}/>
      </div>
    );
  }
}
export default App

如果要练习createPortal的正确用法

记住:createPortal生成的节点必须写在render的返回内容里,跟随组件的渲染流程挂载,它只是改变了节点最终挂载的DOM位置,事件冒泡、React生命周期逻辑依然和正常渲染的节点完全一致:

import React  from 'react'
import ReactDOM from 'react-dom';
class App extends React.Component {
  state = {
    todoList: []
  }
  add = () => {
    const inputEl = document.getElementById("textInput");
    const textVal = inputEl.value.trim();
    if (!textVal) return;
    this.setState({
      todoList: [...this.state.todoList, textVal]
    })
    inputEl.value = '';
  }
  render(){
    return (
      <div>
        To Do list:
        {/* 遍历列表时返回portal节点,会把li挂载到指定的外部DOM节点下 */}
        {this.state.todoList.map((todo, idx) => {
          // 这里可以把li渲染到页面任意已存在的DOM节点上,哪怕在当前组件DOM树外面
          return ReactDOM.createPortal(
            <li key={idx}>{todo}</li>,
            document.getElementById('ul')
          )
        })}
        <input type="text" id="textInput" />
        <input type="button" value="Add" onClick={this.add}/>
      </div>
    );
  }
}
export default App
关键注意点
  • 永远不要在事件回调里单独调用createPortal指望它能直接渲染内容,它的返回值必须作为组件渲染输出的一部分才能生效
  • React下所有UI更新都要通过修改state驱动,不要把React API当成原生DOM操作方法用
  • portal只适合需要跳出父DOM容器限制的场景,普通父子组件渲染不需要使用这个API

内容的提问来源于stack exchange,提问作者Youssef Mohamed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 17:03:22