为什么我的 ReactJs 应用程序没有正确更新
Why isn't my ReactJs App not updating correctly
我正在编写一个食谱应用程序。
食谱的食谱存储在 yaml 文件中,这些文件以静态方式存储。当我加载该站点时,它会自动访问一个 index.json 文件,其中所有食谱都已编入索引,并一个接一个地加载它们并将它们添加到一个数组中。然后将该数组提供给 setRecipe 方法,它应该相应地更新 dom。
这不会发生。
我已经尝试 console.log 了一点,在执行此操作时,我记录了预期的数据,但一旦我刷新页面,情况就不再如此了。正在完成对 yaml 文件的请求。为什么会这样?
完整源代码
import React, { useState, useEffect } from 'react';
import jsyaml from 'js-yaml'
import { ListGroup, ListGroupItem } from 'react-bootstrap';
const basePath = '/k0chbuch'
const recipeStore = basePath + '/recipe-store'
const index = recipeStore + '/index.json'
const RecipeList = () => {
const [recipes, setRecipes] = useState([]);
useEffect(() => {
fetchAllRecipes();
}, []);
const fetchAllRecipes = () => {
fetch(index)
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json()
})
.then(recipeIndex => {
let store = []
recipeIndex.forEach(element => {
fetch(recipeStore + '/' + element + '.yaml')
.then(res => res.blob())
.then(blob => blob.text())
.then(text => jsyaml.load(text))
.then(recipeObject => {
store.push(recipeObject)
})
})
return store
})
.then((all) => setRecipes(all));
}
return (
<div>
<h1>Rezepte:</h1>
<ListGroup>
{recipes.map((r)=>(<ListGroupItem>{r.Titel}</ListGroupItem>))}
</ListGroup>
</div>
);
};
export default RecipeList;
简单的 Yaml 示例:
---
Titel: Hackbraten
Autor: d3v3lop3r
Komplexitaet: 1
Portionen: 4
Zutaten:
- 1000g Hack
- 150g Zwiebeln
- Gewürze nach Wahl
Zubereitung: |
Zuerst wird das Hack gewürzt, dann die Zwiebeln braten und dem Hack zugeben. Kräftig kneten, dann bei 200°C eine Stunde backen.
Kommentar: Schmeckt am besten mit Kartoffeln!
useEffect
基于作为 useEffect
挂钩的第二个参数传递的依赖项数组运行。
试试这个,
const [recipes, setRecipes] = useState([]);
useEffect(() => {
if(!recipes && recipes.length == 0){
fetchAllRecipes();
}
}, [recipes])
这将导致您的 useEffect
挂钩在 recipes
状态发生变化时立即调用,但只会导致 fetchRecipes()
函数在 recipes
状态为null
或数组长度为0
您的问题在于在第二个 .then()
中填充 store
数组:使用 Array.prototype.forEach
时,请记住 return
并没有真正执行任何操作。在解析 promise 之前,您不需要等待 blob 被解析。事实上,菊花链式承诺的这一部分立即解析 :
.then(recipeIndex => {
let store = [];
recipeIndex.forEach(element => {
fetch(...);
});
// `store` is returned immediately without waiting for forEach!
return store;
})
相反,我建议使用 Array.prototype.map
to return an array of promises based off recipeIndex
. Then return Promise.all()
,它确保所有承诺都得到解决:
fetch(index)
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then(recipeIndex => {
const promises = recipeIndex.map((element) => {
return fetch(recipeStore + '/' + element + '.yaml')
.then(res => res.blob())
.then(blob => blob.text())
.then(text => jsyaml.load(text));
});
return Promise.all(promises);
})
.then((all) => setRecipes(all));
使用 for
循环怎么样? (可以,但不推荐)
另一种方法是使用 for
循环,然后您可以使用 async/await。但是我通常不建议这样做,原因有二:
for
loop + await
意味着每个请求都在彼此之后调度并且 不并行 ,这会增加延迟。上面的 Array.prototype.map
解决方案同时分派获取请求。
- 有些 linter 不鼓励在
.then()
中嵌套异步回调
但是,如果您想尝试一下,for
循环是完全可行的:
fetch(index)
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then(async (recipeIndex) => {
const recipes = [];
for (const element of recipeIndex) {
const recipe = await fetch(recipeStore + '/' + element + '.yaml')
.then(res => res.blob())
.then(blob => blob.text())
.then(text => jsyaml.load(text));
recipes.push(recipe);
}
})
.then((all) => setRecipes(all));
我正在编写一个食谱应用程序。 食谱的食谱存储在 yaml 文件中,这些文件以静态方式存储。当我加载该站点时,它会自动访问一个 index.json 文件,其中所有食谱都已编入索引,并一个接一个地加载它们并将它们添加到一个数组中。然后将该数组提供给 setRecipe 方法,它应该相应地更新 dom。 这不会发生。 我已经尝试 console.log 了一点,在执行此操作时,我记录了预期的数据,但一旦我刷新页面,情况就不再如此了。正在完成对 yaml 文件的请求。为什么会这样?
完整源代码
import React, { useState, useEffect } from 'react';
import jsyaml from 'js-yaml'
import { ListGroup, ListGroupItem } from 'react-bootstrap';
const basePath = '/k0chbuch'
const recipeStore = basePath + '/recipe-store'
const index = recipeStore + '/index.json'
const RecipeList = () => {
const [recipes, setRecipes] = useState([]);
useEffect(() => {
fetchAllRecipes();
}, []);
const fetchAllRecipes = () => {
fetch(index)
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json()
})
.then(recipeIndex => {
let store = []
recipeIndex.forEach(element => {
fetch(recipeStore + '/' + element + '.yaml')
.then(res => res.blob())
.then(blob => blob.text())
.then(text => jsyaml.load(text))
.then(recipeObject => {
store.push(recipeObject)
})
})
return store
})
.then((all) => setRecipes(all));
}
return (
<div>
<h1>Rezepte:</h1>
<ListGroup>
{recipes.map((r)=>(<ListGroupItem>{r.Titel}</ListGroupItem>))}
</ListGroup>
</div>
);
};
export default RecipeList;
简单的 Yaml 示例:
---
Titel: Hackbraten
Autor: d3v3lop3r
Komplexitaet: 1
Portionen: 4
Zutaten:
- 1000g Hack
- 150g Zwiebeln
- Gewürze nach Wahl
Zubereitung: |
Zuerst wird das Hack gewürzt, dann die Zwiebeln braten und dem Hack zugeben. Kräftig kneten, dann bei 200°C eine Stunde backen.
Kommentar: Schmeckt am besten mit Kartoffeln!
useEffect
基于作为 useEffect
挂钩的第二个参数传递的依赖项数组运行。
试试这个,
const [recipes, setRecipes] = useState([]);
useEffect(() => {
if(!recipes && recipes.length == 0){
fetchAllRecipes();
}
}, [recipes])
这将导致您的 useEffect
挂钩在 recipes
状态发生变化时立即调用,但只会导致 fetchRecipes()
函数在 recipes
状态为null
或数组长度为0
您的问题在于在第二个 .then()
中填充 store
数组:使用 Array.prototype.forEach
时,请记住 return
并没有真正执行任何操作。在解析 promise 之前,您不需要等待 blob 被解析。事实上,菊花链式承诺的这一部分立即解析 :
.then(recipeIndex => {
let store = [];
recipeIndex.forEach(element => {
fetch(...);
});
// `store` is returned immediately without waiting for forEach!
return store;
})
相反,我建议使用 Array.prototype.map
to return an array of promises based off recipeIndex
. Then return Promise.all()
,它确保所有承诺都得到解决:
fetch(index)
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then(recipeIndex => {
const promises = recipeIndex.map((element) => {
return fetch(recipeStore + '/' + element + '.yaml')
.then(res => res.blob())
.then(blob => blob.text())
.then(text => jsyaml.load(text));
});
return Promise.all(promises);
})
.then((all) => setRecipes(all));
使用 for
循环怎么样? (可以,但不推荐)
另一种方法是使用 for
循环,然后您可以使用 async/await。但是我通常不建议这样做,原因有二:
for
loop +await
意味着每个请求都在彼此之后调度并且 不并行 ,这会增加延迟。上面的Array.prototype.map
解决方案同时分派获取请求。- 有些 linter 不鼓励在
.then()
中嵌套异步回调
但是,如果您想尝试一下,for
循环是完全可行的:
fetch(index)
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then(async (recipeIndex) => {
const recipes = [];
for (const element of recipeIndex) {
const recipe = await fetch(recipeStore + '/' + element + '.yaml')
.then(res => res.blob())
.then(blob => blob.text())
.then(text => jsyaml.load(text));
recipes.push(recipe);
}
})
.then((all) => setRecipes(all));