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

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
}

存在的疑问:

  1. 使用useState和useEffect是否有必要?因为生成并洗牌数组并非副作用。
  2. 尝试在组件外定义变量实现,但所有Board组件会共享同一个数组,无法独立随机,不符合纯函数要求。
  3. 困惑于何时使用组件内外变量及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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 08:15:34