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

React新增列表项时不使用memo/useMemo避免全列表重渲染

问题根因

原代码把jobs数组存在App根组件的state里,每次调用setJobs更新数组都会让整个App组件重跑一遍渲染逻辑,jobs.map会全量执行,所有列表项的渲染代码都重新走一次,自然会重新生成每个条目前面的随机数。
题目不让用memo、useMemo做渲染缓存,解决的核心思路就是把已有列表项和App的重渲染链路切开,别让App更新的时候重新跑已有项的渲染代码。

实现代码

对应HTML依赖:

<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react.development.js"></script>

核心逻辑代码:

const { useRef } = React;

// 独立封装列表组件,自主维护列表DOM
class JobList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  // 对外暴露新增条目的方法
  addItem = (text) => {
    const randomVal = Math.random();
    const newLi = document.createElement('li');
    newLi.textContent = `${text} ${randomVal}`;
    this.listRef.current.appendChild(newLi);
  }

  render() {
    return <ul ref={this.listRef}></ul>
  }
}

function App() {
  const inputRef = useRef(null);
  const listRef = useRef(null);

  const handleAdd = () => {
    const inputVal = inputRef.current.value.trim();
    if (!inputVal) return;
    // 直接调用列表组件的新增方法,不触发App自身重渲染
    listRef.current.addItem(inputVal);
    inputRef.current.value = '';
  }

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleAdd}>Add</button>
      <JobList ref={listRef} />
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'));
方案说明
  • 整个实现没用到memo、useMemo任何一个被禁止的API,完全符合题目限制
  • 把列表的DOM维护逻辑单独封在JobList组件里,App本身不存任何和列表相关的state,点Add按钮的时候根本不会触发App重渲染
  • 加新条目时只创建新的li节点插到ul里,已经存在的li节点完全不会被React的diff逻辑扫到,根本不会重渲染,每个条目刚生成时算出来的随机数会一直保留,加新条目时旧条目的数值一点不会变,完全符合预期效果
  • 虽然直接操作DOM看起来不符合React数据驱动的常规写法,但在题目明确禁止使用缓存类API的约束下,这是性能最高、逻辑最清晰的实现方式,没有多余的渲染开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 16:30:45