我需要加载一个包含26731个元素的JSON文件,然后获取长度,然后生成一个随机元素并使用代码中的值。但这种异步React让我抓狂,我一直在努力让简单的东西同步
问题是,尽管我可以将JSON加载到DICTIONARY:[],但我不能使用它来生成随机数的长度。
我尝试了扩散运算符...this.State,但它看起来不合适,也没有什么不同。
我的州
this.state = {
dictionary: [],
isLoaded: false,
dictLength: 0,
rnd: 0
};
componentDidMount() {
this.loadUpDictionary();
this.DictionaryLength();
this.RandomNumber();
}
这是可行的,因为我可以在渲染中看到一个词典。代码没有问题,只是ComponentDidmount没有按顺序运行函数。
loadUpDictionary() {
loadDictionary().then(d => {
this.setState(() => ({
dictionary: d,
isLoaded: true
}));
// console.log(this.state.dictionary);
});
}
但这些函数不包含任何值,因为在呈现之前,字典不包含任何值
DictionaryLength() {
const Length = this.state.dictionary.length;
console.log("Length of dictionary " + Length); //outputs Length of dictionary 0
this.setState(() => ({
dictLength: Length
}));
}
RandomNumber() {
const min = 0;
const max = this.state.dictLength - 1;
const rnd = Math.floor(Math.random() * (max - min + 1) + min);
this.setState(() => ({
rnd: rnd
}));
}
渲染
render() {
const { isLoaded, dictionary, dictLength, rnd } = this.state; //pass across the state
if (!isLoaded) {
return <div>Loading ....</div>;
} else {
return (
<div>
<h1>
Dictionary loaded {dictionary.Entries.length} RND {rnd} dictLength{" "}
{dictLength}
</h1>
<h2>Get a single entry {dictionary.Entries[3].Word} </h2> means{" "}
{dictionary.Entries[3].Definition}
</div>
);
}
}
输出
字典已加载26731 RND 0口述长度0获取单个条目,表示空闲。高级在船的尾部半部。准备好了。比船尾更靠近船尾。[来自a2,-baft:参见aft]
对我来说,这是最难的React部分。
4条答案
按热度按时间n8ghc7c11#
通常,除非有充分的理由,否则您不希望在一个函数中多次调用
setState
,因为这会降低性能。您需要整理所有数据,然后使用所有数据调用一次setState
。因此,加载字典,计算您的随机数,然后调用setState
一次,同时更新所有4个状态值。类似这样的东西(还没有测试过这个):后续调用将嵌套在加载的
.then
中,但这很好,因为它们无论如何都依赖于它。如果您想在它们自己的同步函数中计算字典长度和随机数,您仍然可以这样做并调用它们,例如
只要让这些函数“返回”值,而不是将其设置为状态即可。
yqhsw0fo2#
问题不在于Reaction,而在于JavaScript是如何工作的
当
this.loadUpDictionary
被执行时,this.DictionaryLength()
将立即被执行。您在loadUpDictionary
中有一个.then
,但它将仅在this.DictionaryLength
和this.RandomNumber
之后执行。因此,快速修复其想法是让
this.loadUpDictionary
和this.setState
首先运行,由于setState
允许回调,因此您可以使用this.DictionaryLength()
来执行此操作dba5bblo3#
您需要了解异步是如何工作的。代码将执行并继续到下一行**,然后**上一个函数完成。因此,在您的场景中,每个函数都会立即运行,而不是等待前一个函数完成。
这就是承诺存在的原因。一些选项--根据您的目标浏览器,使用像Bluebird这样的Promise库并从您的函数返回承诺,或者使用javascript async/await,以便在您尝试在下一步处理结果之前完成您的调用。
然而,查看上面的代码,我建议将字典的处理合并到单个函数中,并从那里设置状态。除非需要,否则我会避免将整本词典加载到该州。类似于:
uurity8g4#
拥抱异步编程!它的好处可以用一个早餐例子来说明。如果你在做烤面包,你是愿意在烤面包机后什么都不做,还是先打开烤面包机,然后拿出橙汁和麦片来等烤面包做好?
与异步编程相同:您可以发出一个HTTP请求并保持与用户的交互,直到数据返回,而不是挂起浏览器。如果您需要发出20个独立的HTTP请求,Async允许您一次发出所有请求,而不是按顺序进行。
您给出的代码似乎非常接近工作流的类型,但主要问题似乎是这个函数:
如果
this.loadUpDictionary()
是异步的,那么上面的模式有争用条件,并且不能保证您的数据在您尝试使用它时准备就绪。一种解决方案是将异步调用的结果链接到then
或await
,这保证了承诺已被解析。这就像是等到吐司烤好了才把果酱放在吐司上--这是一种真正的依赖。话虽如此,确实没有必要将
dictionary
的长度存储在它自己的变量中。您已经有了dictionary.length
属性,所以这只是一个需要担心保持一致的状态。很有可能,
isLoaded
也不是必需的。由于状态更改,Reaction会自动知道加载已完成,并将触发重新渲染。只需对词典发出HTTP请求,并在请求解析时选择一个随机单词。下面是一个最小的完整示例:
下面是一个带有钩子和假请求的可运行代码片段,该请求还会在请求到达时而不是在呈现时选取随机单词: