如何让 useEffect 按预期工作?
How do I get useEffect to work as expected?
我不熟悉如何使用 spoonacular api 来使用端点获取食谱。这是数据示例。
{
vegetarian: true,
vegan: false,
preparationMinutes: 10,
extendedIngredients: [
{original: "1/8 teaspoon ground cardamom"},
{original: "2 to 4 black peppercorns, crushed"},
{original: "1/4 cup sweetened condensed milk or 4 teaspoons sugar"},
{original: "1/4 cup loose Darjeeling tea leaves or 5 tea bags black tea"},
{original: "2 cups water"}
],
id: 165381,
title: "Chai",
readyInMinutes: 10,
servings: 4,
}
出于某种原因,每当我使用 'extendedIngredients' 时都会收到错误消息 'Cannot use properties of undefined',但如果我评论 'extendedIngredients',请保存并再次取消评论并 运行 应用程序, 有效
代码:
const Single=({id})=>{
const [recipe, setRecipe]=useState({})
useEffect(()=>{axios.get(`https://api.spoonacular.com/recipes/${id}/information?apiKey=${apiKey}`)
.then(res=>{
setRecipe(res.data)
console.log("recipe",recipe)
})
.catch(err=>{
console.log(err)
})
},[])
console.log("recipe",recipe)
return(
<div>
<Row>
<Col>
<h1>{recipe.title}</h1>
<p>{recipe.extendedIngredients.length} Ingredients</p>
<p>{recipe.readyInMinutes} Minutes</p>
<p>{recipe.servings} Servings</p>
</Col>
<Col>
<img src={recipe.image} alt={recipe.title}></img>
</Col>
</Row>
<h2>Ingredients</h2>
{recipe.extendedIngredients.map((ingredient,i)=><p key={i}>{ingredient.original}</p>)}
<h2>Directions</h2>
<div dangerouslySetInnerHTML={{__html: recipe.instructions}}/>
</div>
)
}
第一次渲染收据是一个空对象,所以添加加载状态或类似的东西直到你得到响应
编辑:最好将初始值设置为 null
您可以使用可选链接的新功能(没有 Internet Explorer)。
recipe?.extendedIngredients
注意食谱后面的问号。这意味着如果配方未定义,它将退出并且不会尝试读取未定义的属性。
因为您的对象在从 API 获取之前是未定义的。您可以换行条件:
{recipe && <>
<h1>{recipe.title}</h1>
<p>{recipe.extendedIngredients.length} Ingredients</p>
<p>{recipe.readyInMinutes} Minutes</p>
<p>{recipe.servings} Servings</p>
</>}
在加载之前不会尝试渲染配方...
或者您可以在那里设置一些“加载程序”逻辑:
const Single=({id})=>{
const [loading, setLoading] = useState(false)
const [recipe, setRecipe]=useState({})
useEffect(()=>{
setLoading(true);
axios.get(`https://api.spoonacular.com/recipes/${id}/information?apiKey=${apiKey}`)
.then(res=>{
setRecipe(res.data)
console.log("recipe",recipe)
})
.catch(err=>{
console.log(err)
}).finally(()=>setLoading(false));
},[])
然后 return 如果没有加载东西则加载
if(loading)return(<>Loading...</>);
return(<div>
<Row>
<Col>
<h1>{recipe.title}</h1>
<p>{recipe.extendedIngredients.length} Ingredients</p>
<p>{recipe.readyInMinutes} Minutes</p>
<p>{recipe.servings} Servings</p>
</Col>
<Col>
<img src={recipe.image} alt={recipe.title}></img>
</Col>
</Row>
<h2>Ingredients</h2>
{recipe.extendedIngredients.map((ingredient,i)=><p key={i}>{ingredient.original}</p>)}
<h2>Directions</h2>
<div dangerouslySetInnerHTML={{__html: recipe.instructions}}/>
</div>
)
}
如果你看一下你的 useEffect,你提供了一个空的 dependencies 数组作为参数。这将导致回调(您的获取逻辑)在第一次渲染时触发。 Axios 将创建一个 promise,它可能会很快解决,但对于第一次渲染来说还不够快。这意味着,您需要处理网络请求未返回数据的情况。实现此目的的最简单方法是使用如下条件渲染。
使用此设置,在您的承诺解决之前不会呈现任何内容,一旦它完成,React 将按预期呈现您的组件。下一步是使用微调器或某种正在加载数据的指示器。
const Single = ({ id }) => {
const [recipe, setRecipe] = useState({})
useEffect(() => {
axios.get(`https://api.spoonacular.com/recipes/${id}/information?apiKey=${apiKey}`)
.then(res => {
setRecipe(res.data)
console.log("recipe", recipe)
})
.catch(err => {
console.log(err)
})
}, [])
console.log("recipe", recipe)
return (
<div>
{recipe && (
<>
<Row>
<Col>
<h1>{recipe.title}</h1>
<p>{recipe.extendedIngredients.length} Ingredients</p>
<p>{recipe.readyInMinutes} Minutes</p>
<p>{recipe.servings} Servings</p>
</Col>
<Col>
<img src={recipe.image} alt={recipe.title}></img>
</Col>
</Row>
<h2>Ingredients</h2>
{recipe.extendedIngredients.map((ingredient, i) => (
<p key={i}>{ingredient.original}</p>
))}
<h2>Directions</h2>
<div dangerouslySetInnerHTML={{ __html: recipe.instructions }} />
</>
)}
</div>
)
}
我不熟悉如何使用 spoonacular api 来使用端点获取食谱。这是数据示例。
{
vegetarian: true,
vegan: false,
preparationMinutes: 10,
extendedIngredients: [
{original: "1/8 teaspoon ground cardamom"},
{original: "2 to 4 black peppercorns, crushed"},
{original: "1/4 cup sweetened condensed milk or 4 teaspoons sugar"},
{original: "1/4 cup loose Darjeeling tea leaves or 5 tea bags black tea"},
{original: "2 cups water"}
],
id: 165381,
title: "Chai",
readyInMinutes: 10,
servings: 4,
}
出于某种原因,每当我使用 'extendedIngredients' 时都会收到错误消息 'Cannot use properties of undefined',但如果我评论 'extendedIngredients',请保存并再次取消评论并 运行 应用程序, 有效
代码:
const Single=({id})=>{
const [recipe, setRecipe]=useState({})
useEffect(()=>{axios.get(`https://api.spoonacular.com/recipes/${id}/information?apiKey=${apiKey}`)
.then(res=>{
setRecipe(res.data)
console.log("recipe",recipe)
})
.catch(err=>{
console.log(err)
})
},[])
console.log("recipe",recipe)
return(
<div>
<Row>
<Col>
<h1>{recipe.title}</h1>
<p>{recipe.extendedIngredients.length} Ingredients</p>
<p>{recipe.readyInMinutes} Minutes</p>
<p>{recipe.servings} Servings</p>
</Col>
<Col>
<img src={recipe.image} alt={recipe.title}></img>
</Col>
</Row>
<h2>Ingredients</h2>
{recipe.extendedIngredients.map((ingredient,i)=><p key={i}>{ingredient.original}</p>)}
<h2>Directions</h2>
<div dangerouslySetInnerHTML={{__html: recipe.instructions}}/>
</div>
)
}
第一次渲染收据是一个空对象,所以添加加载状态或类似的东西直到你得到响应
编辑:最好将初始值设置为 null
您可以使用可选链接的新功能(没有 Internet Explorer)。
recipe?.extendedIngredients
注意食谱后面的问号。这意味着如果配方未定义,它将退出并且不会尝试读取未定义的属性。
因为您的对象在从 API 获取之前是未定义的。您可以换行条件:
{recipe && <>
<h1>{recipe.title}</h1>
<p>{recipe.extendedIngredients.length} Ingredients</p>
<p>{recipe.readyInMinutes} Minutes</p>
<p>{recipe.servings} Servings</p>
</>}
在加载之前不会尝试渲染配方...
或者您可以在那里设置一些“加载程序”逻辑:
const Single=({id})=>{
const [loading, setLoading] = useState(false)
const [recipe, setRecipe]=useState({})
useEffect(()=>{
setLoading(true);
axios.get(`https://api.spoonacular.com/recipes/${id}/information?apiKey=${apiKey}`)
.then(res=>{
setRecipe(res.data)
console.log("recipe",recipe)
})
.catch(err=>{
console.log(err)
}).finally(()=>setLoading(false));
},[])
然后 return 如果没有加载东西则加载
if(loading)return(<>Loading...</>);
return(<div>
<Row>
<Col>
<h1>{recipe.title}</h1>
<p>{recipe.extendedIngredients.length} Ingredients</p>
<p>{recipe.readyInMinutes} Minutes</p>
<p>{recipe.servings} Servings</p>
</Col>
<Col>
<img src={recipe.image} alt={recipe.title}></img>
</Col>
</Row>
<h2>Ingredients</h2>
{recipe.extendedIngredients.map((ingredient,i)=><p key={i}>{ingredient.original}</p>)}
<h2>Directions</h2>
<div dangerouslySetInnerHTML={{__html: recipe.instructions}}/>
</div>
)
}
如果你看一下你的 useEffect,你提供了一个空的 dependencies 数组作为参数。这将导致回调(您的获取逻辑)在第一次渲染时触发。 Axios 将创建一个 promise,它可能会很快解决,但对于第一次渲染来说还不够快。这意味着,您需要处理网络请求未返回数据的情况。实现此目的的最简单方法是使用如下条件渲染。
使用此设置,在您的承诺解决之前不会呈现任何内容,一旦它完成,React 将按预期呈现您的组件。下一步是使用微调器或某种正在加载数据的指示器。
const Single = ({ id }) => {
const [recipe, setRecipe] = useState({})
useEffect(() => {
axios.get(`https://api.spoonacular.com/recipes/${id}/information?apiKey=${apiKey}`)
.then(res => {
setRecipe(res.data)
console.log("recipe", recipe)
})
.catch(err => {
console.log(err)
})
}, [])
console.log("recipe", recipe)
return (
<div>
{recipe && (
<>
<Row>
<Col>
<h1>{recipe.title}</h1>
<p>{recipe.extendedIngredients.length} Ingredients</p>
<p>{recipe.readyInMinutes} Minutes</p>
<p>{recipe.servings} Servings</p>
</Col>
<Col>
<img src={recipe.image} alt={recipe.title}></img>
</Col>
</Row>
<h2>Ingredients</h2>
{recipe.extendedIngredients.map((ingredient, i) => (
<p key={i}>{ingredient.original}</p>
))}
<h2>Directions</h2>
<div dangerouslySetInnerHTML={{ __html: recipe.instructions }} />
</>
)}
</div>
)
}