react-apollo-hooks icon indicating copy to clipboard operation
react-apollo-hooks copied to clipboard

useQuery causes a component to render multiple times

Open frangolet opened this issue 6 years ago • 31 comments

Is it normal with this new hooks api to re-render a component multiple times? I just saw that useQuery causes a component to re-render 3 times. Here is an example: https://codesandbox.io/s/j7kw103525

It's the same, example from the readme file, I have just added a console log to /views/Details.js

const Detail = ({
  match: {
    params: { breed, id }
  }
}) => {
  const { data, error } = useQuery(GET_DOG, {
    variables: { breed }
  });
  console.log(2);
  return (
    <View style={styles.container}>
      <Header text={breed} />
      {error ? (
        <Error />
      ) : (
        <DogList...

And the output is: 2 2 2

I have tried on my own project and got the same result.

frangolet avatar Dec 17 '18 22:12 frangolet

Render with cache-data, render while loading, render with data-result = 3 times. -- That makes sense to me! How many times would you expect it to render? @asfwebmaster

fbartho avatar Dec 18 '18 00:12 fbartho

Hm, I have just updated with: console.log(data, error, loading); and the result is:

Object {dog: Object}
dog: Object
images: Array[169]
__typename: "Dog"
undefined
false
---------------------
Object {dog: Object}
dog: Object
images: Array[169]
__typename: "Dog"
undefined
false
---------------------
Object {dog: Object}
dog: Object
images: Array[169]
__typename: "Dog"
undefined
false

So the loading part is false and all other props are same for all renders, this seems a bit strange. @fbartho

frangolet avatar Dec 18 '18 10:12 frangolet

It looks like this issue has something to do with suspense. I was able to find a limited workaround by using React.memo:

  <Suspense fallback={<div>Loading...</div>}>
    <MyComponent />
  </Suspense>
...
const MyComponent = React.memo(() => {
  const { data, error } = useQuery(QUERY)
  if (error) {
    return `Error! ${error.message}`; 
  }
  return 'MyComponent'
})

In that case, MyComponent renders only once. But this seems to break if you pass children, e.g.:

  <Suspense fallback={<div>Loading...</div>}>
    <MyComponent>{props.children}</MyComponent>
  </Suspense>
...
const MyComponent = React.memo(({ children }) => {
  const { data, error } = useQuery(QUERY)
  if (error) {
    return `Error! ${error.message}`; 
  }
  return children
})

mbrowne avatar Feb 01 '19 16:02 mbrowne

Any word on this?

aadamsx avatar Feb 18 '19 18:02 aadamsx

Have you tested this with environment set to production? It might not be the reason here but React renders components that use hooks twice for dev/safety reasons when environment isn't production.

adjourn avatar Feb 18 '19 19:02 adjourn

Have anyone found a solution or a workaround except using React.memo which doesn't help to fully avoid the issue? This behavior makes my app very slow, because heavy components rerender multiple times without the need.

yantakus avatar May 08 '19 12:05 yantakus

Hello guys :) , I will leave this little help here, what I am doing is using the useApolloClient with useEffect and useState for cases when I need to do stuff with other hooks like for example a form who use a hook to load initial values, is working fine and just renders once, check:

... more imports 
import React, { useEffect, useState } from 'react';
import { useApolloClient } from 'react-apollo-hooks';


const StartService = ({ id }) => {

  const client = useApolloClient();
  const [collections, setCollections] = useState([]);
  const [service, setService] = useState({});
  const [submitting, setSubmitting] = useState(false);

  const [formState, { text, select }] = useFormState();

  useEffect(() => {
    getCollectionType();
    getService();
  }, []);

  const getService = async () => {
    if (id) {
      const {
        data: { getService },
      } = await client.query({
        query: GET_SERVICE_QUERY,
        variables: { id },
      });

      setService(getService);

      if (getService) {
        formState.setField('source', getService.source || '');
        formState.setField('record', getService.record || '');
        formState.setField('licensePlate', getService.licensePlate || '');
      }
    }
  };

  const getCollectionType = async () => {
    const {
      data: { collectionsType },
    } = await client.query({
      query: COLLECTIONS_TYPE_QUERY,
    });

    setCollections(collectionsType);
  };

return (...)

Renders just once, just how I like it 😉

hipdev avatar May 09 '19 00:05 hipdev

@adjourn I think you meant this behaviour. But this happens only if <React.StrictMode> is enabled. I don't use Strict Mode and I still have lots of rerenders. So this is not the case here.

yantakus avatar May 13 '19 07:05 yantakus

I have the same problem. useQuery causes 2 renders even if the query result is already cached.

Alexandredc avatar May 23 '19 12:05 Alexandredc

This issue doesn't exist on @apollo/react-hooks@beta

Alexandredc avatar Jun 03 '19 09:06 Alexandredc

guys please be aware of React.StrictMode ... it will add a couple of extra renders :) ... in my case they jumped from 2 to 6 :)

rares-lupascu avatar Jun 07 '19 14:06 rares-lupascu

Even in production the query causes my component to re-render 4 times, which is two more than expected right? @apollo/react-hooks doesn't have this issue, but i'm having other issues that mean i still can't use that :(

JClackett avatar Jul 22 '19 18:07 JClackett

Is this problem solved somehow?

juicylevel avatar Oct 04 '19 09:10 juicylevel

use @apollo/react-hooks :)

JClackett avatar Oct 04 '19 09:10 JClackett

@JClackett Are you no longer having the other issues that prevented you from using @apollo/react-hooks?

twelve17 avatar Oct 11 '19 16:10 twelve17

@twelve17 @apollo/react-hooks works for me now, so have been using that in all my projects

JClackett avatar Oct 12 '19 14:10 JClackett

I am still having this issue

dubcs avatar Nov 06 '19 00:11 dubcs

This is still happening ! any one got solution to this problem ?

SyedAsimAliSE avatar Apr 06 '20 06:04 SyedAsimAliSE

Should be using the official Apollo react hooks by now man!

JClackett avatar Apr 06 '20 08:04 JClackett

Should be using the official Apollo react hooks by now man!

Ahhh, my bad; i was so much tired to even check the package name ! :P i am using the official one.

SyedAsimAliSE avatar Apr 08 '20 01:04 SyedAsimAliSE

I'm using newest "@apollo/client": "3.0.0-beta.43" and still some additional rerenders happend. After disabling React.StrictMode works fine.

ppsirius avatar Apr 21 '20 12:04 ppsirius

useQuery renders twice for me when I use fetchPolicy: 'cache-only'. Using 'cache-first' produces just one render.

const { data } = useQuery(GET_DATA, {
  fetchPolicy: 'cache-only' //causes second render
});

robertvorthman avatar Jul 21 '20 09:07 robertvorthman

For me also was the problem React.StrictMode

sujed avatar Jan 10 '21 20:01 sujed

same here

torontochen avatar Feb 02 '21 21:02 torontochen

Did someone found a way to solve this problem? I'm also facing this issue.

clovisdasilvaneto avatar Feb 15 '21 11:02 clovisdasilvaneto

Remove Strict Mode Hoc but is just a workaround ;)

ppsirius avatar Feb 15 '21 11:02 ppsirius

@ppsirius I'm not using Strict Mode on my app, just did a very fresh example using CRA and I still have the same problem

clovisdasilvaneto avatar Feb 15 '21 12:02 clovisdasilvaneto

Same here! Not using Strict Mode. Still have 2 renders after a mutation!!!

amkazan avatar Mar 16 '21 08:03 amkazan

you can pass in {enabled: false} like following and it disables extra re-rendering useQuery('key', () => axios.get(), {enabled: false};

SafiUllahAshfaq avatar Jul 24 '21 14:07 SafiUllahAshfaq

useQuery(yourQuery, { fetchPolicy: 'no-cache' })

pgriscti avatar Nov 11 '21 13:11 pgriscti