如何执行一个api调用后与setState在useEffect?



我是一个React新手(30小时学习)和一些基本的Javascript背景。现在我正在学习一门课程,并试图"离开"。的路径。但我很好奇我的预期目标是如何实现的。

有一个Memegenerator,在渲染开始时从API获取所有模因的图像。这个问题可以通过使用effect - hook来解决。现在我想要函数getMemeImage()在api调用后开始运行一次,状态更新(这不是课程的一部分,但我想尝试一下)。

但是它给了我一个错误:"不能在一个未安装的组件上执行React状态更新">

在我的研究中,我发现像didMount(虽然我不理解)之类的东西并不是"现代的"。主要使用钩子的反应方式,你可以简单地使用第二个useEffect。但不幸的是,这对我不起作用。

我该如何解决这个问题,是否有一个"简单"的方法?这是初学者的方法还是高级的东西?我想也许可以使用超时函数,但似乎是非常糟糕的编码。

import React from "react"
export default function Meme() {
const [meme, setMeme] = React.useState({
topText: "",
bottomText: "",
randomImage: "" 
})
const [allMemes, setAllMemes] = React.useState([])
React.useEffect(() => {   /* first useEffect */
fetch("https://api.imgflip.com/get_memes")
.then(res => res.json())
.then(data => setAllMemes(data.data.memes))
}, [])


React.useEffect(() => {   /* This should run after the setAllMemes in the first useEffect was complete */
getMemeImage()
}, [allMemes])

function getMemeImage() {
const randomNumber = Math.floor(Math.random() * allMemes.length)
const url = allMemes[randomNumber].url
setMeme(prevMeme => ({
...prevMeme,
randomImage: url
}))

}
function handleChange(event) {
const {name, value} = event.target
setMeme(prevMeme => ({
...prevMeme,
[name]: value
}))
}
return (
<main>
<div className="form">
<input 
type="text"
placeholder="Top text"
className="form--input"
name="topText"
value={meme.topText}
onChange={handleChange}
/>
<input 
type="text"
placeholder="Bottom text"
className="form--input"
name="bottomText"
value={meme.bottomText}
onChange={handleChange}
/>
<button 
className="form--button"
onClick={getMemeImage}
>
Get a new meme image 🖼
</button>
</div>
<div className="meme">
<img src={meme.randomImage} className="meme--image" />
<h2 className="meme--text top">{meme.topText}</h2>
<h2 className="meme--text bottom">{meme.bottomText}</h2>
</div>
</main>
)
}

首先,"无法在未挂载的组件上执行React状态更新";是一个警告,而不是错误。

为了只在第一个useEffect钩子执行后调用'getMemeImage',您可以检查在第一个useEffect执行后和收到异步函数响应后发生变化的二进制标志的值。

import React from "react";
export default function Meme() {
const [meme, setMeme] = React.useState({
topText: "",
bottomText: "",
randomImage: ""
});
const [allMemes, setAllMemes] = React.useState([]);
const isInitialRender = React.useRef(true);
React.useEffect(() => {
fetch("https://api.imgflip.com/get_memes")
.then((res) => res.json())
.then((data) => {
isInitialRender.current = false;
setAllMemes(data.data.memes);
});
}, []);
React.useEffect(() => {
/* Check if it is called before the first useEffect data fetching was completed */
if (!isInitialRender.current) getMemeImage();
}, [allMemes]);
function getMemeImage() {
const randomNumber = Math.floor(Math.random() * allMemes.length);
const url = allMemes[randomNumber].url;
setMeme((prevMeme) => ({
...prevMeme,
randomImage: url
}));
}
}

使用useRef钩子在渲染之间持久化它引用的值。

你可以把控制添加到你的第二个useEffect,它会解决你的问题。这是工作示例https://codesandbox.io/s/heuristic-matan-3kdbmv

React.useEffect(() => {
if (allMemes.length > 0) {
getMemeImage();
}
}, [allMemes]);

最新更新