在 ReactJS 中渲染一对随机选择的对象(某些属性不匹配)作为扑克牌



我正在做一个小游戏,用户可以猜测两种食物中哪一种含有最多的卡路里。我抓取了 400+ 食品的数据并导入到我的组件中。

我想确保两种随机选择的食物没有相同数量的卡路里。我在 componentDidMount 中放了一个 while 循环来检查它是否属实。如果为 true,它将 State 设置为另一个随机选择的食物 - 重复直到它们不再是相同的数字。

现在,我将 while 循环的条件设置为"!=="进行测试。在我知道它以另一种方式工作后,这将更改为"==="。我遇到的问题是,在我的控制台中,食物将始终是相同的对象,因为 setState 由于某种原因不起作用。

import Foods from "../foods.json";
class Game extends Component {
state = {
firstFood: Foods[Math.floor(Math.random() * Foods.length)],
secondFood: Foods[Math.floor(Math.random() * Foods.length)],
mostCalories: "placeholder"
};
componentDidMount() {
while (
// to check that the two foods' calories are not the same
this.state.firstFood.attributes.calories !==
this.state.secondFood.attributes.calories
) {
console.log(this.state.firstFood.attributes.calories);
console.log(this.state.secondFood.attributes.calories);
debugger;
// an infinite loop because the below setState function doesn't run properly
this.setState({
firstFood: Foods[Math.floor(Math.random() * Foods.length)]
});
}
}

我面临的另一个问题是,在我的调试器中,当我尝试手动输入Foods[Math.floor(Math.random() * Foods.length)]时,我得到一个"食物未定义",尽管它在状态初始化中工作。但是,当我从自动填充在控制台中输入_foods_json__WEBPACK_IMPORTED_MODULE_1__时,我的所有数据都在那里,所以这有效(替换了"食物"一词(:

_foods_json__WEBPACK_IMPORTED_MODULE_1__[Math.floor(Math.random() * _foods_json__WEBPACK_IMPORTED_MODULE_1__.length)]

不过,我不能在 React 中使用那行代码。

http://www.giphy.com/gifs/UqwKkdF2ok2hlTwlLB

我建议稍微不同的方法来生成一对伪随机项目,这可能不会节省您的 CPU 周期,但肯定会避免导致您当前位置的循环。

所以,这个想法相当简单:

  • 你选择第一个随机项目
  • 然后你过滤掉具有相同营养价值的项目
  • 您从剩余的物品中随机选择另一个物品
pickTwo = arr => {
const firstItem = arr[0|Math.random()*arr.length],
filteredArr = arr.filter(({value}) => firstItem.value != value),
secondItem = filteredArr[0|Math.random()*filteredArr.length]
return [firstItem, secondItem]
}

您可以在下面找到该概念的演示:

//dependencies
const { useState, useEffect } = React,
{ render } = ReactDOM
//sample data set
const data = [{name:'BBQ Ranch Burger',value:350},{name:'Big Mac',value:540},{name:'Double Cheesburger',value:440},{name:'Bacon Cheddar McChicken',value:540},{name:'Chicken McNuggets (10 piece)',value:440},{name:'Bacon Biscuit',value:350}]
//food card component
const FoodCard = ({name,value}) => (
<div style={{width:100,height:150,display:'table-cell', border:'1px solid black',verticalAlign:'middle',textAlign:'center'}}>{name}<br/>(calories: {value})</div>
)
//game board component
const GameBoard = () => {
//store foods within local state
const [foods,setFoods] = useState([]),
//describe thw way of picking two pseudo-random items
pickTwo = arr => {
const firstItem = arr[0|Math.random()*arr.length],
filteredArr = arr.filter(({value}) => firstItem.value != value),
secondItem = filteredArr[0|Math.random()*filteredArr.length]
return [firstItem, secondItem]
}
//pick two cards on initial render      
useEffect(() => setFoods(pickTwo(data)),[])
//return component
return (
<div>
<FoodCard {...foods[0]} />
<FoodCard {...foods[1]} />
<button onClick={() => setFoods(pickTwo(data))}>reset</button>
</div>
)
}
render(
<GameBoard />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

你的循环会无休止地运行的原因是,当你调用setState状态实际上并没有立即更新时,它有一个滞后,这与 React 组件的生命周期有关。

因此,而不是在循环中调用 setState。声明一个局部变量,更新循环中的变量,当循环完成后,然后设置状态。

componentDidMount() {
let myFood = this.state.firstFood
while (
// to check that the two foods' calories are not the same
myFood.attributes.calories !==
this.state.secondFood.attributes.calories
) {
myFood = Foods[Math.floor(Math.random() * Foods.length)]
}
this.setState({
firstFood: myFood
});
}

我没有对此进行详细测试,但这应该会给您带来想法。

状态不仅仅是您随时设置的变量。它是反应性的。它会导致重新渲染。你应该选择两种你想吃的食物,确保它们的卡路里不同,然后你才setState它们

没有理由setStatecomponentDidMount,你的逻辑属于constructor

const _pickFoods = (allFoods) => {
const firstFood = allFoods[Math.floor(Math.random() * allFoods.length)]
let secondFood
do {
secondFood = allFoods[Math.floor(Math.random() * allFoods.length)]
} while (firstFood.attributes.calories !== secondFood.attributes.calories)
return { firstFood, secondFood }
}
class Game  extends Component {
constructor(props) {
super(props)
const { firstFood, secondFood } = _pickFoods(Foods)
this.state = {
firstFood,
secondFood
}
}
...
}

也就是说,如果你想要这个,同时在你的食物中逻辑。如果您的食物数组仅包含不可存活的食物,它将永远循环。试试这个更好的选择

const _pickFoods = (allFoods) => {
const firstFood = allFoods[Math.floor(Math.random() * allFoods.length)]
const possibleSecondFoods = allFoods.filter(food => food !== firstFood && food.attributes.calories !== firstFood.attributes.calories)
const secondFood = possibleSecondFoods[Math.floor(Math.random() * possibleSecondFoods.length)]
return secondFood
}

试试这个:

import Foods from "../foods.json";
class Game extends Component {
state = {
firstFood: Foods[Math.floor(Math.random() * Foods.length)],
secondFood: Foods[Math.floor(Math.random() * Foods.length)],
mostCalories: "placeholder"
};
componentDidMount() {
setTimeout( () => {
while (
// to check that the two foods' calories are not the same
this.state.firstFood.attributes.calories !==
this.state.secondFood.attributes.calories
) {
console.log(this.state.firstFood.attributes.calories);
console.log(this.state.secondFood.attributes.calories);
debugger;
// an infinite loop because the below setState function doesn't run properly
this.setState({
firstFood: Foods[Math.floor(Math.random() * Foods.length)]
});
}
},2000)
}

我认为这是因为数据还没有准备好。

最新更新