question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

useQuery state change does not trigger useEffect

See original GitHub issue

Intended outcome :

As soon as the data is fetched, trigger the useEffect (pass the data/ the loading state from the useQuery hook in the dep array).

Present outcome:

data or loading state change does not trigger the useEffect hook, although the component is rendered.

This is a very simplistic version of the problem :

const MockGraph = () => {

    const { data, loading, error } = useQuery(fiveDayPrices);

    const [stockData, setStockData] = useState([]);

    useEffect(() => {
        if (data) {
            console.log("useEffect was triggered after useQuery state change");
            setStockData(data.getFiveDatPrices);
        };
    }, [data]);


    if (loading) return <div>Loading...</div>;
    if (error) return  <div>Error</div>;

    console.log("stockData,before data fetch: ",stockData);
    if (data){

        console.log("stockData,after data fetch: ",stockData);

        return (
            <div className="bg-slate-900 w-screen h-screen">
    
                {/* The Graph */}
                <div className='flex justify-center pt-9 z-0 relative'>
                    <GraphExample
                        width={400}
                        height={350}
                        stockData={stockData}
                        loading={loading} />
                </div>
            </div>
        );
    };
};

Thank you !

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
jerelmillercommented, Sep 30, 2022

Hey @vpaul18! Thanks for some more of the context! Would you be willing to share the Apollo client and React version version you’re using? That would go a long way toward helping us understand if it’s an Apollo bug or something else!

when the fastest query (the five-day one) solves, but it crashes - as the stockData initially passed is always an empty array

Could you expand on this a bit? What kind of error are you seeing in this case?

I’m not sure if your code matches exactly what I see above, but in your example, I see that your component will return undefined when fiveDaysAgoPricesData is not set. Up until React 18, returning undefined would result in an error. Just wanted to confirm the crash was not because of this reason 🙂


@bignimbus and I went ahead and tried to create a minimal reproduction of your issue to see if we could get the useEffect to fire when getting data back from a useQuery. You can see our little app we created using this sandbox that tries to recreate the issue. We’ve got 2 different queries that tries to replicate the issue you’re seeing.

The first uses a query with no variables:

export default function App() {
  // ...
  const [countries, setCountries] = useState([]);
  const { data, loading } = useQuery(COUNTRIES_QUERY);

  useEffect(() => {
    if (data) {
      setCountries(data.countries);
    }
  }, [data]);

  // ...
}

The second uses a query with a single variable, to check that the useEffect fires multiple times when the query data has changed.

const Country = ({ code }) => {
  const [country, setCountry] = useState(null);
  const { data } = useQuery(COUNTRY_QUERY, { variables: { code } });

  useEffect(() => {
    if (data) {
      setCountry(data.country);
    }
  }, [data]);

  // ...
}

As you can see while playing around with the app, this seems to work as expected.

It seems our app is a bit more simplistic than yours given that you mention you’re sending a couple queries out together and trying to use that data in useEffect. Would you take a look at the CodeSandbox above and fork it to try and recreate the issue you’re seeing? Perhaps there is something we missed that we’d be able to further investigate.


A couple things I noted looking at your code samples above as well. I’m willing to accept these are copy paste errors, so please let me know if that is the case!

In one of the code snippets you posted in the followup, you have this bit of code:

useEffect(() => {
  if (fullHistoricPricesData) {
    setStockData(...);
  } else {
    if (fiveDaysAgoPricesData) {
      setStockData(fiveDaysAgoPricesData.getFiveDaysAgoPrices);
    }
  }
}, [fiveDaysAgoPricesData, dataHistoryChoice]);

Here I notice that your deps passed to useEffect do not include the fullHistoricPricesData, which you use in your if statement. Given this code, the useEffect won’t fire if the fullHistoricPricesData changes. Perhaps see if adding this to deps fixes the issue.

If you are using ESLint, I’d also highly recommend using the eslint-plugin-react-hooks package as it can help find these particular issues. I’ve made this mistake more times than I can count 🙈 and this ESLint package has really helped me make sure I haven’t missed something.

Again, totally understand if this is a copy/paste error! Just wanted to point that out in case that ended up being the issue 🙂 .

Something else I noticed with this particular code snippet is that you’re passing in dataHistoryChoice to your useEffect, though I don’t see you using that value in the logic. It appears that you’re always using fullHistoricPricesData regardless of what the dataHistoryChoice ends up being. Unless you’re conditionally querying for fullHistoricPricesData based on the value of dataHistoryChoice, you may find yourself with a bug here as this code will not be able to use fiveDaysAgoPricesData as long as fullHistoricPricesData is set.

You may consider switching your conditional to use the dataHistoryChoice instead. Here is a rough idea:

useEffect(() => {
  if (dataHistoryChoice === '5D') {
    setStockData(fiveDaysAgoPricesData);
  } else {
    setStockData(fullHistoricPricesData);
  }
}, [fiveDaysAgoPricesData, dataHistoryChoice, fullHistoricPricesData]);

All that said, as a best practice, I’d caution against using useState for derived data as it means you’ll need to keep that state up to date any time one of its dependencies change. Instead, consider deriving the stockData in render (rather than useEffect + useState):

const App = () => {
  // ...
  const stockData = dataHistoryChoice === '5D'
    ? fiveDaysAgoPricesData
    : fullHistoricPricesData

  if (!stockData) {
    return null;
  }

  return (
    <div className="bg-slate-900 w-screen h-screen">
      <div className='flex justify-center pt-9 z-0 relative'>
        <GraphExample
          width={400}
          height={350}
          stockData={stockData}
          handleTabChange={handleTabChange}
        />
      </div>
    </div>
  );
}

Obviously I don’t have the full context of your app, so feel free to ignore any of the advice given here. Just wanted to give some guidance based on some observations I made from the code pasted above! I hope we can get to the bottom of your issue soon 🙂

0reactions
jerelmillercommented, Oct 1, 2022

You’re very welcome! Glad we could help where we can!

Read more comments on GitHub >

github_iconTop Results From Across the Web

How do you handle useQuery with useEffect? - Help
I need to edit a form with data coming from useQuery. ... blank pages sometimes where loading state change seems not to trigger...
Read more >
Bug: useEffect not triggering on [deps] change #2628 - GitHub
The problem is that one state change gets swallowed by useEffect. It can be seen if you click on a button and watch...
Read more >
Apollo query not triggering useEffect - reactjs - Stack Overflow
Data loading from a graphql query is not one of the triggers. useEffect will run : After re-rendering of the layout when the...
Read more >
useHooks - Easy to understand React Hook recipes
We bring you easy to understand React Hook code recipes so you can learn how React hooks work and feel more comfortable writing...
Read more >
Setting React State after useQuery? : r/graphql - Reddit
I am unable to use the data property returned from Apollo useQuery because the input field using has two way binding.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found