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

React中状态变更时如何避免App组件不必要重渲染?

解决Todo应用中App组件不必要重渲染的最优方案

核心思路

要避免App组件因任务数组变更导致的全量重渲染,关键是隔离状态变更的影响范围,通过组件缓存、稳定回调引用、优化状态更新逻辑这几个方向入手。

具体实现步骤

1. 拆分独立渲染组件并缓存

把任务列表的渲染逻辑从App组件中拆分出来,单独封装成TodoList组件,并用React.memo包裹。这样只有当传入TodoList的props(任务数组、编辑回调等)发生实际变化时,组件才会重渲染,而非随App组件无条件重渲染。

示例:

// TodoList.jsx
import React from 'react';
import Task from './Task';

const TodoList = React.memo(({ todoArr, editTask }) => {
  return (
    <div className="todo-list">
      {todoArr.map(task => (
        <Task key={task.id} task={task} editTask={editTask} />
      ))}
    </div>
  );
});

export default TodoList;

2. 用useCallback稳定编辑回调的引用

如果editTask直接定义在App组件中,每次App重渲染都会生成新的函数实例,导致子组件接收到的props变化,触发不必要的重渲染。用useCallback包裹回调函数,确保其引用稳定;同时使用函数式状态更新,避免直接依赖todoArr,进一步减少回调的依赖项。

修改App组件逻辑:

import { useState, useCallback } from 'react';
import TodoList from './TodoList';

export default function App() {
  const [todoArr, setTodoArr] = useState(getTasksFromLs());

  // 用useCallback包裹,函数式更新获取最新状态
  const editTask = useCallback((e, id, inpValue) => {
    e.preventDefault();
    setTodoArr(prevArr => prevArr.map(task => 
      task.id !== id ? task : { ...task, text: inpValue }
    ));
    
    // 同步更新localStorage
    const LS_task = JSON.parse(localStorage.getItem(id));
    LS_task.text = inpValue;
    localStorage.setItem(id, JSON.stringify(LS_task));
  }, []); // 无依赖项,引用始终稳定

  return (
    <div className="app">
      {/* App级别的其他内容(如添加任务表单) */}
      <TodoList todoArr={todoArr} editTask={editTask} />
    </div>
  );
}

3. 优化Task组件的props接收

同样用React.memo包裹Task组件,确保只有当task数据或editTask回调发生变化时,单个Task组件才会重渲染:

// Task.jsx
import React, { useState } from 'react';

const Task = React.memo(({ task, editTask }) => {
  const [inpValue, setInpValue] = useState(task.text);
  const [isEditing, setIsEditing] = useState(false);

  const handleEditSubmit = (e) => {
    editTask(e, task.id, inpValue);
    setIsEditing(false);
  };

  return (
    <div className="task">
      {isEditing ? (
        <form onSubmit={handleEditSubmit}>
          <input 
            type="text" 
            value={inpValue} 
            onChange={(e) => setInpValue(e.target.value)} 
          />
          <button type="submit">保存</button>
        </form>
      ) : (
        <div>
          <span>{task.text}</span>
          <button onClick={() => setIsEditing(true)}>编辑</button>
        </div>
      )}
    </div>
  );
});

export default Task;

额外注意点

  • 修正代码拼写错误:你代码里的texst应为text,避免数据更新异常。
  • 如果App组件还有其他局部状态(比如添加任务的输入框状态),建议将这些状态下移到对应子组件中,进一步减少App组件的重渲染触发条件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 11:27:28