Home > Software design >  Rerender component on click
Rerender component on click

Time:09-12

I'm trying to generate a new quote when the button is clicked. Having trouble figuring out how to implement this. Googling around has led me to believe that the useCallback hook is the way to go, but I haven't any experience with it so I have yet to have any luck implementing it. Any help is appreciated! Thank you in advance.

/* eslint-disable react-hooks/rules-of-hooks */
import React, { useEffect, useState, useCallback } from 'react'

const Main = () => {

 const [quote, setQuote] = useState(null)
 const [author, setAuthor] = useState(null)
 const [newQuote, setNewQuote] = useState(false)
 useEffect(() => {
    fetch('https://type.fit/api/quotes')
     .then(response => response.json())
     .then((data) => {
        let randomIndex = Math.floor((Math.random() * data.length));
    
        setQuote(data[randomIndex].text)
        setAuthor(data[randomIndex].author)
        
      })
     .catch(err => console.error(err));
    
  }, [])

  return (
    <div id='main' className='grid place-items-center h-screen w-screen text-center'>
     {/* Quote Gen Container */}
     <div className='flex flex-col justify-start mx-auto bg-sky-300 w-3/4 h-3/4 text-black space-y-3 p-32 rounded-3xl relative'>
      <h1 className='text-bold text-3xl absolute top-0 mx-auto'>Random Quote Generator</h1>
      <div>
        <h4 id="text">{`"${quote}"`}</h4>
      </div>
      <div>
        <p id="author">{`- ${author}`}</p>
      </div>
      <div id="button">
        <button onClick={() => setNewQuote(null)} className='bg-black text-white rounded-xl p-2 abs'>New Quote</button>
      </div>
     </div>
    </div>
  )
}

export default Main

CodePudding user response:

Refactor the fetch logic into a callback that can be called from either the useEffect hook or directly in a button's onClick handler.

const Main = () => {
  const [quote, setQuote] = useState({});

  // Memoize a stable callback function reference
  const fetchQuote = useCallback(() => {
    fetch('https://type.fit/api/quotes')
      .then(response => response.json())
      .then((data) => {
        const randomIndex = Math.floor((Math.random() * data.length));
    
        setQuote(quotes[randomIndex]);
      })
      .catch(err => console.error(err));
  }, []);

  // Initial fetch of quotes
  useEffect(() => {
    fetchQuote();
  }, [fetchQuote]);

  if (!quote) return null;

  return (
    <div id='main' className='....'>
     {/* Quote Gen Container */}
     <div className='....'>
      <h1 className='....'>Random Quote Generator</h1>
      <div>
        <h4 id="text">{`"${quote.text}"`}</h4>
      </div>
      <div>
        <p id="author">{`- ${quote.author}`}</p>
      </div>
      <div id="button">
        <button
          onClick={fetchQuote} // <-- attach callback to fetch quote
          className='....'>
          New Quote
        </button>
      </div>
     </div>
    </div>
  )
}

If the fetched data doesn't change then fetch it once and select a random quote.

const Main = () => {
  const [quotes, setQuotes] = useState([]);
  const [quote, setQuote] = useState(null);

  // Memoize function to set single random quote
  const getQuote = useCallback(() => {
    const randomIndex = Math.floor((Math.random() * quotes.length));
    setQuote(quotes[randomIndex]);
  }, [quotes]);

  // Mounting useEffect to fetch and save all quotes
  // and set initial random quote
  useEffect(() => {
    fetch('https://type.fit/api/quotes')
      .then(response => response.json())
      .then((data) => {
        setQuotes(data);

        const randomIndex = Math.floor((Math.random() * data.length));
        setQuote(data[randomIndex]);
      })
      .catch(err => console.error(err));
  }, []);

  if (!quote) return null;

  return (
    <div id='main' className='....'>
     {/* Quote Gen Container */}
     <div className='....'>
      <h1 className='....'>Random Quote Generator</h1>
      <div>
        <h4 id="text">{`"${quote.text}"`}</h4>
      </div>
      <div>
        <p id="author">{`- ${quote.author}`}</p>
      </div>
      <div id="button">
        <button
          onClick={getQuote}
          className='....'
        >
          New Quote
        </button>
      </div>
     </div>
    </div>
  )
}

CodePudding user response:

Assuming you don’t need to re-fetch the data every time you could store the returned data in state, then have your onclick just choose a new random entry to set as the “current” quote.

  • Related