React Hooks实现shift+click多选任务时如何避免所有TaskItem重渲染
React Hooks shift+click批量选中任务性能优化方案

问题核心
原有实现将selectedTaskIds加入useCallback依赖,导致每次选中任务后selectTask函数引用更新,所有TaskItem因props变化触发全量重渲染。
优化方案
- 利用useState函数式更新特性,在更新回调中获取最新的
selectedTaskIds值,无需将其加入useCallback依赖 - 用
React.memo包装TaskItem组件,仅当自身props变化时才触发重渲染 - 可选:若
tasks数组会频繁变更,可通过useRef存储tasks的最新引用,进一步移除useCallback对tasks的依赖
优化后代码
父组件逻辑
const [selectedTaskIds, setSelectedTaskIds] = useState<string[]>([]) // 可选:如果tasks会频繁变化,加这个ref存最新tasks,可移除useCallback对tasks的依赖 const tasksRef = useRef(tasks) useEffect(() => { tasksRef.current = tasks }, [tasks]) const selectTask = useCallback( (e: MouseEvent<HTMLDivElement>, taskId: string): void => { e.stopPropagation() // 用setState函数式更新获取最新的选中列表 setSelectedTaskIds(prevSelected => { const previousTaskId = prevSelected[prevSelected.length - 1] if (previousTaskId && e.shiftKey) { // 用了tasksRef的话这里替换为tasksRef.current即可 const previousIdx = tasks.findIndex((task) => task.id === previousTaskId) const selectedIdx = tasks.findIndex((task) => task.id === taskId) const rangeTasks = previousIdx < selectedIdx ? tasks.slice(previousIdx, selectedIdx + 1) : tasks.slice(selectedIdx, previousIdx + 1) const rangeIds = rangeTasks.map((task) => task.id) return [...new Set([...prevSelected, ...rangeIds])] } else { return [taskId] } }) }, [tasks] // 用tasksRef的话这里依赖数组填空即可 ) return ( {tasks.map((task) => ( <TaskItem key={task.id} taskId={task.id} onClick={selectTask} active={selectedTaskIds.includes(task.id)} /> ))} )
TaskItem组件包装
// 用React.memo做浅比较,只有props变化时才会触发重渲染 const TaskItem = React.memo(({ taskId, onClick, active }) => { // 原有组件逻辑不变 })
优化效果说明
selectTask函数引用仅会在tasks变更时更新(用ref存储tasks的话全程引用不变),不会随选中操作频繁变化- 每个
TaskItem仅当自身active状态变化时才会触发重渲染,不会出现全量重渲染问题 - 原有shift+click批量选中的功能逻辑完全不变
内容的提问来源于stack exchange,提问作者JimmyJS
相关产品推荐
相关产品推荐

