React中useEffect生成随机奖品数组的用法是否正确?代码优化咨询
问题描述
需要在Board组件中,利用传入的props参数prizes数组生成一个长度为16的随机奖品数组并展示。prizes数组结构如下:
[ { prizeId: 1, name: 'coupon', image: 'img/coupon.svg', }, { prizeId: 2, name: 'gift card', image: 'img/gift-card.svg', }, // more prizes ]
最初实现的Board组件代码:
const Board = ({ prizes }) => { const [shuffledPrizes, setShuffledPrizes] = useState(null) useEffect(() => { setShuffledPrizes(shuffleArray(populatePrize(16, prizes))) }, [prizes]) return ( <div> { shuffledPrizes && shuffledPrizes.map((prize) => ( <Prize key={prize.id} prize={prize} /> )) } </div> ) }
为解决重复奖品的React key问题,在populatePrize函数中添加了唯一id:
import { nanoid } from 'nanoid' const populatePrize = (noOfBlock, prizeArray) => { const arrayToPopulate = [] let index = 0 for (let i = 0; i < noOfBlock; i += 1, index += 1) { if (index === prizeArray.length) { index = 0 } arrayToPopulate.push({ id: nanoid(), prizeId: prizeArray[index].prizeId, name: prizeArray[index].name, image: prizeArray[index].image, }) } return arrayToPopulate }
存在的疑问:
- 使用
useState和useEffect是否有必要?因为生成并洗牌数组并非副作用。 - 尝试在组件外定义变量实现,但所有Board组件会共享同一个数组,无法独立随机,不符合纯函数要求。
- 困惑于何时使用组件内外变量及state,希望学习正确的React优化方式。
解决方案与解释
1. 最初的useState+useEffect是合理的
生成洗牌后的数组虽然不是传统的副作用(比如网络请求、DOM操作),但它是带随机性的计算。如果直接在组件渲染中执行随机逻辑,会导致每次组件渲染(父组件传参变化、自身状态更新)都重新生成数组,引发不必要的重渲染甚至UI闪烁。
用useState存储结果、useEffect监听prizes变化时重新计算,能保证:
- 随机数组仅在
prizes更新时重新生成,避免重复计算 - 每个Board组件实例拥有独立的随机数组状态
2. 组件外变量的问题
组件外的变量属于模块级作用域,所有组件实例会共享这个变量,违背了React组件独立性的要求——每个实例应拥有自己的状态和计算结果,不能互相干扰。这种写法不仅无法实现独立随机,还会导致不可预测的bug。
3. 优化后的正确实现
方案一:用useMemo替代useState+useEffect
如果不需要后续修改随机数组,用useMemo缓存派生计算结果更简洁,能避免重复执行随机逻辑:
const Board = ({ prizes }) => { // 仅当prizes变化时重新生成并洗牌数组 const shuffledPrizes = useMemo(() => { return shuffleArray(populatePrize(16, prizes)) }, [prizes]) return ( <div> {shuffledPrizes.map((prize) => ( <Prize key={prize.id} prize={prize} /> ))} </div> ) }
方案二:保留useState+useEffect(适合需修改数组的场景)
如果后续需要修改随机数组(比如抽奖后替换奖品),保留useState更合适,因为它允许通过setShuffledPrizes更新状态:
const Board = ({ prizes }) => { const [shuffledPrizes, setShuffledPrizes] = useState([]) useEffect(() => { setShuffledPrizes(shuffleArray(populatePrize(16, prizes))) }, [prizes]) // 示例:后续可调用此方法刷新奖品数组 const refreshPrizes = () => { setShuffledPrizes(shuffleArray(populatePrize(16, prizes))) } return ( <div> {shuffledPrizes.map((prize) => ( <Prize key={prize.id} prize={prize} /> ))} <button onClick={refreshPrizes}>刷新奖品</button> </div> ) }
4. 组件内外变量与state的使用场景
- 组件外变量:仅用于存储全局共享的常量或无状态工具函数(比如
populatePrize、shuffleArray这类纯函数),绝对不能存储组件实例的状态或依赖props的计算结果。 - 组件内局部变量:用于渲染过程中的临时计算(比如循环临时变量),每次渲染都会重新创建,不会在渲染周期间保留。
- React State(useState):用于存储需要在渲染周期间保留、影响UI展示,或后续需要修改的值。
- useMemo/useCallback:用于缓存昂贵的计算结果或函数,避免重复执行,优化性能,适用于基于props/state的派生计算。
内容的提问来源于stack exchange,提问作者Htet Oo Wai Yan
相关产品推荐
相关产品推荐

