Home > database >  useEffect fetches data after using the data | ReactJS
useEffect fetches data after using the data | ReactJS

Time:08-11

I am creating a random quote generator. Everything works fine. But the quote doesn't appear at the page reload. I have to click new quote button to get a new quote. It should also display a quote when the DOM is loaded.

My Code:

import { useCallback, useEffect, useState } from 'react';
import './App.css';

function App() {
     const quotesURL ='https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json';

const [quotes, setQuotes] = useState([]);
const [randomQuote, setRandomQuote] = useState('');

const fetchQuotes = useCallback(() => {
    fetch(quotesURL)
        .then(response => response.json())
        .then(data => setQuotes(data.quotes))
        .catch(err => console.error(err));

    // console.log(quotes);
}, [quotesURL]);

// eslint-disable-next-line react-hooks/exhaustive-deps
const handleNewQuote = () => {
    const randomIndex = Math.floor(Math.random() * quotes.length);
    setRandomQuote(quotes[randomIndex]);
};

useEffect(() => {
    fetchQuotes();
}, []);

return (
    <div>
        <div id='quote-box'>
            <div id='text'>{randomQuote.quote}</div>
            <div id='author'>{randomQuote.author}</div>
            <button id='new-quote' onClick={handleNewQuote}>
                New Quote
            </button>
            <a href='https://www.twitter.com/intent/tweet' id='tweet-quote'>
                Tweet Quote
            </a>
        </div>
        {quotes?.map((quote, index) => (
            <div key={index}>{JSON.stringify(quote)}</div>
        ))}
    </div>
);
}

export default App;

What I want is to display a quote at the reload of the page. It currently works only when the button is clicked!

The problem according to my understanding is that the useEffect calls fetchQuotes() after handleNewQuote(), so there is no quote to load

CodePudding user response:

Try including the following in your component:

useEffect(() => {
    if (!randomQuote) {
        const randomIndex = Math.floor(Math.random() * quotes.length);
        setRandomQuote(quotes[randomIndex]);
    }
}, [quotes]);

This runs when the quotes have been loaded and but random quote is still null.

Or even better, call setRandomQuote() at the time you fetch quotes.

const fetchQuotes = useCallback(() => {
    fetch(quotesURL)
        .then(response => response.json())
        .then(data => {
           const randomIndex = Math.floor(Math.random() * data.quotes.length);
           setRandomQuote(data.quotes[randomIndex]);
           setQuotes(data.quotes)
        })
        .catch(err => console.error(err));
}, [quotesURL]);

Sandbox

CodePudding user response:

There's a lot of ways to do it, but I think the best one would be to set random quote when the data is loaded. It can be done after fetch or in useEffect

So I'd do following

const handleNewQuote = useCallback(() => {
    const randomIndex = Math.floor(Math.random() * quotes.length);
    setRandomQuote(quotes[randomIndex]);
}, [quotes, setRandomQuote])

// useCallback so it can be passed to useEffect safely without infinite loop (memoize function when component rerenders)



useEffect(() => {
  handleNewQuote()
}, [quotes, handleNewQuote])
  • Related