'useEffect keeps fetching data and re-rendering the component
I just started building projects this week and fresh outta tutorial hell so please go easy, my code might be rough too.
I'm trying to render a "randomAdvice" in the component on button click, but it just keep re-rendering and making API calls.
This doesn't happen when I console log, only when I try to add the Advice to the component to be displayed.
code is below
function App() {
const [randomAdvice, setRandomAdvice] = useState({
id: '',
advice: '',
});
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.adviceslip.com/advice');
const data = await response.json();
setRandomAdvice(data);
};
fetchData();
}, [randomAdvice]);
const setAdvice = () => {
setRandomAdvice({ ...randomAdvice, advice: randomAdvice.slip.advice });
};
const setId = () => {
setRandomAdvice({ ...randomAdvice, advice: randomAdvice.slip.id });
};
const getAdvice = () => {
setAdvice();
setId();
console.log(randomAdvice.slip.advice, randomAdvice.slip.id);
};
// work well when I click the button it gets logged to the console but once I try to uncomment so it renders on the dom, it just keeps re-rendering
return (
<div className='App'>
<p>How far</p>
{/* {randomAdvice && <p>{randomAdvice.slip.advice}</p>}
{randomAdvice && <p>{randomAdvice.slip.id}</p>} */}
<button onClick={getAdvice}>generate random advice</button>
</div>
);
Solution 1:[1]
Your code produces an infinite re-rendering due to useEffect implementation. You are updating a state which is also a dependency of your useEffect hence it triggers a re-render.
I have updated your code to prevent the issue:
function App() {
const [randomAdvice, setRandomAdvice] = useState({
id: '',
advice: '',
});
const fetchData = async () => {
const response = await fetch('https://api.adviceslip.com/advice');
const { slip } = await response.json();
setRandomAdvice(slip);
};
const getAdvice = () => {
fetchData();
};
return (
<div className="App">
<p>How far</p>
{randomAdvice && <p>{randomAdvice.advice}</p>}
{randomAdvice && <p>{randomAdvice.id}</p>}
<button onClick={getAdvice}>generate random advice</button>
</div>
);
}
Also, make sure to have a handler when a user clicks the button to prevent sending multiple requests at the same time. For example, setting it to disabled once the promise is not yet resolved.
EDIT: I have also deconstructed slip from response to match with your state structure.
EDIT: You can also simplify your return statement using the code below:
<div className="App">
<p>How far</p>
{randomAdvice && (
<>
<p>{randomAdvice.advice}</p>
<p>{randomAdvice.id}</p>
</>
)}
<button onClick={getAdvice}>generate random advice</button>
</div>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
