React: useEffect Dependencies

Hi all,

I am running into an issue with my app, which is a random quote generator. It’s supposed to work like this: The page starts off with a random quote and primary color, plus a Twitter URL is generated which the user can click allowing them to Tweet the quote. Click the “New Quote” and a new random color appears plus a new quote and corresponding Twitter URL. Here’s the code I have so far:

import React, {useState, useEffect} from "react"
import './App.scss';

function App() {
const [data, setData] = useState([])
const [quote, setQuote] = useState("")
const [author, setAuthor] = useState("")
const [primaryColor, setPrimaryColor] = useState("")
const [twitterLink, setTwitterLink] = useState("")

const colorArray = ["light-blue", "dark-blue", "light-green", "dark-green", "light-pink", "dark-pink"]

useEffect(() => {
  fetch("https://type.fit/api/quotes")
  .then(response => response.json())
  .then(data => {
    setData(data)
    getRandomQuote()
    getPrimaryColor()
    getTwitterLink()
  })
}, [])

const getPrimaryColor = () => {
  const newColorArray = colorArray.filter(color => color !== primaryColor)
  const randomColorIndex = Math.floor(Math.random() * newColorArray.length)
  setPrimaryColor(newColorArray[randomColorIndex])
}

const getTwitterLink = () => {
  const appendedUrl = quote.replace(/ /g, "+")
  setTwitterLink(`https://twitter.com/intent/tweet/?text=${appendedUrl}`)
}

const getRandomQuote = () => {
  const newData = data.filter(object => object.author !== null)
  const randomQuoteIndex = Math.floor(Math.random() * newData.length)
  setQuote(newData[randomQuoteIndex].text)
  setAuthor(newData[randomQuoteIndex].author)
}

const handleClick = () => {
  getRandomQuote()
  getPrimaryColor()
  getTwitterLink()
}

  return (
    <div className={primaryColor} id="app">
      <div id="quote-box">
        <h1 id="text">{quote}</h1>
        <h2 id="author">&#8212; {author}</h2>
        <div className="bottom">
        <a href={twitterLink} target="_blank" rel="noreferrer"><i className={`fab fa-twitter ${primaryColor}`}></i></a>
        <button className={primaryColor} onClick={handleClick}>New Quote</button>
        </div>
      </div>
    </div>
  );
}
export default App;

The problem is, the functions that control getting the new quote (getRandomQuote), the random color (getRandomColor) and the Twitter URL (getTwitterUrl) need to be in useEffect somehow, because when the page initially loads, I need to have a quote, color, and my Twitter URL, but if I put these functions in the dependancies array I create an infinite loop.

I guess I could just write out these functions inside of useEffect, but I still need to use those three functions (getRandomQuote, getPrimaryColor, getTwitterUrl) each time I click the “Get Quote” button and I can’t access my functions if they’re defined within another function. I feel like defining the same functions/constants twice would be a big no-no but I’m not really sure what else to do here. React is fairly new to me, and I’ve still got a lot to learn so I appreciate any and all help.

Hello there,

If you are replicating componentDidMount(), then you should not put anything in the dependency array - as you have it now. So, just do not put anything in there.

There should be no need to define a function twice. So, what is wrong with your code as it is now? The only thing I can see, which I do not understand is why you have handleClick within parentheses in the onClick callback?

Thanks for catching the parenthesis around the handleClick, that was a typo that I had missed, and also yes, I am trying to replicate a componentDidMount(). Besides that, I still receive the following error:

React Hook useEffect has missing dependencies: ‘getPrimaryColor’, ‘getRandomQuote’, and ‘getTwitterLink’. Either include them or remove the dependency array react-hooks/exhaustive-deps

That is a fun little linting warning. It is not an error, and you can either:

  1. Ignore it
  2. Add this to your code:
...
    getTwitterLink()
  })
/* eslint-disable-next-line react-hooks/exhaustive-deps */
}, [])

Hope this helps

Ah okay, when I added the following it did not work:

/* eslint-disable-next-line exhaustive-deps */

But when I modified it to the following it did work:

/* eslint-disable-next-line */

Either way this helps because I didn’t realize I could just ignore the message, so thank you that makes my life a lot easier now!

Sorry about that. I should have double checked, the correct way to do it is:

/* eslint-disable-next-line react-hooks/exhaustive-deps */

I will update my earlier post to reflect this.

That’s alright. Although, when I do put the code in there and click on the little “x” button from the promt:

I notice that my app is empty, because the state hasn’t been updated:

Once I click on the “New Quote” button, things start working properly again:

I’m still not sure how to get my functions running properly on the initial screen load.

I have not tested, but I assume this is happening because setData is asynchonous, so getRandomQuote() > data is undefined.

What I would test, is, within your getRandomQuote, just add a console.log(newData) on line 38.

Hope this helps

1 Like

Yes, when I log it to the console it is undefined, but from there I don’t know what to do to resolve the issue

@Sky020 is correct.

You cannot use the functions below setData right away because setData is async function. So on getRandomQuote execution, the data will be undefined.

setData(data)
 getRandomQuote();
 getPrimaryColor();
 getTwitterLink();

To solve that, you can remove these functions and put them into another useEffect like this:

  useEffect(() => {
    fetch('https://type.fit/api/quotes')
      .then((response) => response.json())
      .then((data) => {
        setData(data);
      });
  }, []);

  useEffect(() => {
    if (data.length) {
      getRandomQuote();
      getPrimaryColor();
      getTwitterLink();
    }
  }, [data]);

There we explicitly ask to run those functions when data.length is available.

1 Like

That worked! Thank you so much, I did not know that it was okay to use useEffect more than once, but what you wrote makes sense. :slightly_smiling_face:

Also @Sky020, thank you very much for your help too. I appreciate you both!

1 Like

You are welcome! I also encourage you NOT to ignore the dependencies error message. It is there in purpose. Try to find a solution and learn why it is happening. It is very important. Do not hesitate to contact me if you need help!

1 Like

Will do on both fronts!

1 Like