我在这里使用useEffect生成数组是否正确



我想使用prizes数组生成一个16长度的随机奖品数组,该数组在Board组件中作为道具传递,并显示它们。

prizes阵列-

[
{
prizeId: 1,
name: 'coupon',
image: 'img/coupon.svg',
},
{
prizeId: 2,
name: 'gift card',
image: 'img/gift-card.svg',
},
// more prizes
]

Board.js-中

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>
)
}

populatePrize函数中,我必须添加id作为React密钥,因为已经存在的prizeId无法使用,因为奖品将重复-

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函数之外使用一个变量,比如-

let shuffledPrizes = null
const Board = ({ prizes }) => {
if (!shuffledPrizes)
shuffledPrizes = shuffleArray(populatePrize(16, prizes))
}
return (
<div>
{
shuffledPrizes.map((prize) => (
<Prize
key={prize.id}
prize={prize}
/>
))
}
</div>
)
}

但是,通过这种方式,每个<Board />组件都引用并显示相同的shuffledPrizes阵列,而不是像我想要的那样随机地用于每个Board组件。

重用Board不是一个要求,但我在React文档中读到组件是纯函数的,我不认为我的是。我还对何时在组件外部或内部使用变量以及何时使用state感到困惑。

虽然我的问题可能是关于使用useEffect,但我想学习如何以正确的React方式改进此代码。

这确实不是一个很好的useEffect用例。

效果是React范式的一个逃生舱口。他们让你"走出去"React,并将您的组件与一些外部系统,如非React小部件、网络或浏览器DOM。如果不涉及外部系统(例如,如果当某些道具或状态发生变化时更新组件的状态(不应该需要效果。删除不必要的影响将使代码更易于遵循,运行速度更快,而且不易出错。

当你通过道具时,你可以洗牌。

const BoardContainer = () => <div>
<Board prizes={shuffleArray(populatePrize(16, prices))}/>
<Board prizes={shuffleArray(populatePrize(16, prices))}/>
</div>

您也可以使用useState的惰性版本,该版本仅在第一次渲染时评估

const Board = ({prizes}) => {
const [shuffledPrizes,] = useState(() => shuffleArray(populatePrize(16, prizes)))
return (
<div>
<ul>
{
shuffledPrizes && shuffledPrizes.map((prize) => (
<Prize
key={prize.id}
prize={prize}
/>
))
}
</ul>
</div>
)
}

您的奖品是以道具形式提供的,因此它们可能会被更新?通过取东西或类似的东西
在这种情况下,您可以:


cont defaultArray = []; // avoid to trigger useEffect at each update with a new array in initialization 
const Board = ({ prizes = defaultArray }) => {
const [shuffledPrizes, setShuffledPrizes] = useState([])
useEffect(() => {
if(prizes.length) {
setShuffledPrizes(shuffleArray(populatePrize(16, prizes)));
}
}, [prizes]);
return (
<div>
{
shuffledPrizes.map((prize) => (
<Prize
key={prize.id}
prize={prize}
/>
))
}
</div>
)
}

如果你这样做:

const Board = ({ prizes }) => {
const shuffledPrizes = shuffleArray(populatePrize(16, prizes))
return (
<div>
{
shuffledPrizes.map((prize) => (
<Prize
key={prize.id}
prize={prize}
/>
))
}
</div>
)
}

populatePrize和shuffleArray将在每次渲染时调用。如果你唯一的道具是价格,并且你使用React.memo,也许它会起作用。但我认为它更难维护。

这样从组件中生成一个变量,不会让组件监听这个变量的修改。可以对常量执行此操作。

测试每个渲染!shuffledPrize,所以当它被填充一次时,你的变量也会被填充,你的组件也会正确渲染。但如果您更改奖品,shuffled奖品将不会更新。这不是一个好的做法。

使用不同的条件,可以继续更新外部组件变量,以侦听触发渲染的道具更改。但如果道具发生变化,useEffect是更好的监听方式。

在您发布的代码中,shuffledPrizes可以为null,因此您应该在调用.map()之前设置一个条件

就我自己而言,我会调用存储在is状态的父级中的suffle函数,以通过shuffling直接存储它,而不是在错误的转发器上调用shuffle函数。

最新更新