我正在做一个小游戏,用户可以猜测两种食物中哪一种含有最多的卡路里。我抓取了 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
它们
没有理由setState
componentDidMount
,你的逻辑属于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)
}
我认为这是因为数据还没有准备好。