-1

I'm creating a random quotes generator but there's a problem. It just doesn't generate a random quote at the beginning. Please correct me so that the generate random quote works at least one time when rendering

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

    useEffect(() => {
        async function fetchData() {
            await fetch(process.env.NEXT_PUBLIC_API_URL)
                .then(res => res.json())
                .then(data => setQuotes(data))
                .catch(err => console.error(err));
        }

        fetchData();
        console.log('from useEffect', quotes);
        generateRandomQuote();
    }, []);

    console.log('After useEffect(): ', quotes);

    const generateRandomQuote = () => {
        const randomIndex = Math.floor(Math.random() * quotes.length);
        const selectedQuote = quotes[randomIndex];
        setRandomQuote(selectedQuote);
    };
Ali Bin Naseer
  • 113
  • 2
  • 8
  • 2
    You dont wait for the async fetch to end. Try putting generateRandomQuote() in a then(). – Bufer Jul 01 '23 at 22:34
  • 2
    Or even better, go with `async` and `await` for all the asynchronous stuff. Mixing `.then()` and `async` is a recipe for confusion. – Pointy Jul 01 '23 at 22:39
  • 2
    No matter if you await or not, it won't work because state is updated in the next render cycle – Konrad Jul 02 '23 at 00:30
  • @Konrad so what's the solution? – Ali Bin Naseer Jul 02 '23 at 12:46
  • 1
    1. Remove `.then(data => setQuotes(data))` 2. `const data = fetchData();` 3. `generateRandomQuote(data);` 4. `const generateRandomQuote = (quotes) => {` – Konrad Jul 03 '23 at 07:14

3 Answers3

1

To complete what Usman Ali Syed said, if the quotes array is not presented in the UI and you just want the quotes array to be persistent between renderings, you can use useRef instead, here is the example:

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

useEffect(() => {
    async function fetchData() {
        await fetch(process.env.NEXT_PUBLIC_API_URL)
            .then(res => res.json())
            .then(data =>quotes.current = data)
            .catch(err => console.error(err));
    }

    fetchData();
    console.log('from useEffect', quotes);
    generateRandomQuote();
}, []);

console.log('After useEffect(): ', quotes);

const generateRandomQuote = () => {
    ...
    const selectedQuote = quotes.current[randomIndex];
    setRandomQuote(selectedQuote);
};

hope that helps a little bit!

0

You don't have to define the function fetchData inside the useEffect() that's kind of odd. Secondly the fetchData seems to be an async function but you are not awaiting it.

await fetchData()

or

fetchData().then(()=>generateRandomQuote())

EDIT: Konrad is right, you are attempting to use the state variable right after updating it but it won't work because a state update causes the component to re render. Seems to me like you don't really need to have a "quotes" state variable. Inside of the fetchData function you can simply set the randomQuote variable which will be reflected on the frontend if you are displaying it there. If you plan on doing something with the state variable after updating it you are going to have to put it in the useEffect dependency array.

-1

Do not use then with async/await. use either one but not both at once.

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

useEffect(() => { 
   async()=>{ 
    await fetchData(); 
    console.log('from useEffect', quotes); 
    generateRandomQuote(); 
   } 
}, []);

async function fetchData() {
       const res = await fetch(process.env.NEXT_PUBLIC_API_URL);
       const data = await res.json();
       setQuotes(data);
    } 

console.log('After useEffect(): ', quotes); 

const generateRandomQuote = () => { const randomIndex = Math.floor(Math.random() * quotes.length); 
const selectedQuote = quotes[randomIndex]; setRandomQuote(selectedQuote); 
};
Bufer
  • 85
  • 1
  • 8