React组件优化:如何控制兄弟组件的重渲染?
解决方案:优化重渲染并修复勾选状态丢失问题
核心问题分析
- 状态更新逻辑错误:直接用
[...wandsChecked, veh]更新状态,既没处理取消勾选的场景,还因闭包问题导致useCallback里的wandsChecked始终是初始空数组,每次点击只会保留当前魔杖,之前的勾选丢失,看起来像是被取消。 - 不必要的重渲染:父组件
Wands状态更新时,所有Wand子组件都会跟着重渲染,未做渲染优化。 - 复选框状态绑定错误:用
value属性控制勾选状态不符合React规范,应该用checked属性。
分步解决
1. 修复状态更新逻辑(支持选中/取消)
使用setWandsChecked的函数式更新规避闭包问题,同时判断当前魔杖是否已在勾选列表中,实现添加/移除的双向逻辑:
const toggleWandChecked = useCallback((wand) => { setWandsChecked(prev => { // 判断是否已勾选,已勾选则移除,否则添加 return prev.some(item => item.name === wand.name) ? prev.filter(item => item.name !== wand.name) : [...prev, wand]; }); }, []);
2. 用React.memo包裹Wand组件
React.memo会对比组件的props,只有当props变化时才触发重渲染。因为toggleWandChecked用useCallback包裹且依赖为空,wand属性稳定,所以只有当前点击的Wand组件会重渲染(更新自身勾选状态),其他组件不会。
3. 正确绑定复选框的checked状态
根据当前魔杖是否在wandsChecked列表中,直接设置checked属性:
<input type="checkbox" id={wand.name} checked={wandsChecked.some(item => item.name === wand.name)} onClick={() => toggleWandChecked(wand)} />
修改后的完整代码
import { useState, useCallback, memo } from 'react'; import WandsChecked from './WandsChecked'; function Wands ({wands}) { console.log('Wands re-rendering'); const [wandsChecked, setWandsChecked] = useState([]); // 优化后的状态更新函数,支持选中/取消,避免闭包问题 const toggleWandChecked = useCallback((wand) => { setWandsChecked(prev => { const isChecked = prev.some(item => item.name === wand.name); return isChecked ? prev.filter(item => item.name !== wand.name) : [...prev, wand]; }); }, []); return ( <main> <div className='grid gap-4 grid-cols-3 grid-rows-3'> {wands?.map(wand => ( <Wand key={wand.name} // 必须添加唯一key,避免React渲染混乱 wand={wand} isChecked={wandsChecked.some(item => item.name === wand.name)} onToggle={toggleWandChecked} /> ))} </div> <div className='grid gap-4 grid-cols-3 grid-rows-3'> <WandsChecked WandsToCompare={wandsChecked}/> </div> </main> ) } // 用memo包裹,避免不必要的重渲染 const Wand = memo(({ wand, isChecked, onToggle }) => { console.log(`Wand ${wand.name} re-rendering`); return ( <div className='wand-wrapper basis-1/4'> <div className='wand-banner'> <input type="checkbox" id={wand.name} checked={isChecked} onClick={() => onToggle(wand)} /> <label htmlFor={wand.name}>{wand.name}</label> </div> </div> ) }); export default Wands;
额外优化点
- 添加了
key属性:遍历渲染组件时必须添加唯一key,帮助React识别组件身份,优化渲染性能。 - 将
isChecked状态直接传递给Wand组件,避免子组件重复计算,简化逻辑。 - 移除了不必要的
async,状态更新是同步操作,无需异步函数。
内容的提问来源于stack exchange,提问作者QuietCoder
相关产品推荐
相关产品推荐

