React父组件更新multiplier时如何阻止StarAnimation不必要重渲染?
问题描述
我开发了一个带有星星动画、火箭和可更新multiplier的Crash赌博组件。每次调用startCrash更新multiplier状态时,明明和这个状态无关的StarAnimation子组件也会跟着重渲染。以下是相关代码:
父组件变量定义
const starSpeed = { 'slow': 1, 'medium': 0.7, 'fast': 0.2, } const {money, setMoney} = useContext(MoneyContext); const [multiplier, setMultiplier] = useState(1.00); const [starHeightMultiplier, setStarHeightMutliplier] = useState(3) const [starAnimationDuration, setStarAnimationDuration] = useState(starSpeed.slow) const [rocketMargin, setRocketMargin] = useState(60)
更新multiplier的函数
function startCrash() { let timeOut = 500; const crashEnd = Math.floor(Math.random() * (1000 - 1 + 1)) + 1; timeOut = 300 for (let i = 101; i < crashEnd; i++) { setTimeout(() => { setMultiplier(i/100) }, timeOut) timeOut += 300; } }
父组件返回内容
<> <div className={styles.crashGambleWrapper}> <BetMenu money={money} startCrash={startCrash}></BetMenu> <h2 className={styles.betMultiplierNum}>{multiplier.toFixed(2)}x</h2> <div className={styles.rocketGameWrapper}> <div className={styles.rocketWrapper}> <h1>CRASH GAMBLE</h1> <div className={styles.rocketDiv}> <StarAnimation starAnimationDuration={starAnimationDuration} starHeightMultiplier={starHeightMultiplier}></StarAnimation> <div className={styles.rocket} style={{marginTop: `${rocketMargin}vh`, animationDuration: '1s'}}> <img src={rocket}></img> </div> </div> </div> </div> </div> </>
StarAnimation子组件代码
<> {[...Array(40).keys()].map(() => { let x = Math.floor(Math.random() * window.innerWidth*0.73) let h = Math.random() * starHeightMultiplier; let duration = Math.random() * 0.9 + starAnimationDuration; return <i style={{ left: `${x}px`, width: `1px`, top: '1px', height: `${h}px`, animationDuration: `${duration}s` }}></i> })} </>
解决方法
要阻止StarAnimation的不必要重渲染,核心是让它只在依赖的props变化时才更新,具体操作如下:
- 用React.memo包裹StarAnimation组件
React.memo会对组件传入的props做浅比较,只有当props确实变化时,才会触发组件重渲染。修改后的StarAnimation组件如下:
import { memo } from 'react'; const StarAnimation = memo(({ starAnimationDuration, starHeightMultiplier }) => { return ( <> {[...Array(40).keys()].map(() => { let x = Math.floor(Math.random() * window.innerWidth*0.73) let h = Math.random() * starHeightMultiplier; let duration = Math.random() * 0.9 + starAnimationDuration; return <i style={{ left: `${x}px`, width: `1px`, top: '1px', height: `${h}px`, animationDuration: `${duration}s` }}></i> })} </> ) }) export default StarAnimation;
- 可选优化:避免渲染时生成新数组
当前组件里[...Array(40).keys()]每次渲染都会生成新数组,虽然不会触发React.memo的重渲染,但可以提前定义这个数组来减少不必要的计算:
// 放在组件外部,避免每次渲染生成新数组 const STAR_INDEXES = [...Array(40).keys()]; const StarAnimation = memo(({ starAnimationDuration, starHeightMultiplier }) => { return ( <> {STAR_INDEXES.map(() => { // 原有逻辑不变 })} </> ) })
这样处理后,父组件更新multiplier状态时,StarAnimation的props没有变化,就不会触发无意义的重渲染了。
内容的提问来源于stack exchange,提问作者Иван Йорданов
相关产品推荐
相关产品推荐

