Stop `useMutation` from re-rendering component and ignore result
Asked Answered
F

4

7

I have a mutation called like this (this isn't the actual mutation call, but a minimal example):

const App = () => {
  const [myMutation] = useMutation(gql`
    mutation Test($updateUserId: ID!, $updateUserInput: UpdateUserInput!) {
      updateUser(id: $updateUserId, input: $updateUserInput) {
        id
        firstname
        age
      }
    }
  `);

  // Runs every time this component is rendered
  console.log("rendered");

  // Called when the button is clicked
  const update = () => {
    myMutation({
      variables: {
        updateUserId: 1,
        updateUserInput: {
          age: Math.round(Math.random() * 10 + 5) // Set a random age from 5 - 15
        }
      }
    });
  };

  return (
    <>
      <h1>Update Test</h1>
      <button onClick={update}>Update!</button>
    </>
  );
};

Whenever onClick is called, the whole component is re-rendered. This is not what I want, as I don't care about the result of the mutation. Is there any way to stop myMutation from causing a re-render, and ignore the result completely?

Fattish answered 5/9, 2021 at 0:24 Comment(8)
Usually, useMutation does not trigger re-render until you are subscribed to some values inside the cache.Signorina
@RyanLe What do you mean by this? Can you give an example?Fattish
I would need to see the bigger picture of your app. Do you have a link to your repo?Signorina
@RyanLe Sorry, I don't have a link to my repo. Is it possible to demonstrate it using CodeSandbox, or something like that? I also can try to make a basic skeleton of how my program works if that's what you need.Fattish
Sure, that's will do. You could try to reproduce it by codesanbox. I will jump right in.Signorina
@RyanLe I've made a really barebones example: codesandbox.io/s/eloquent-chandrasekhar-xtpky?file=/src/App.js - whenever the update button is clicked, the console shows that the component is re-rendered.Fattish
Let us continue this discussion in chat.Fattish
I put an answer below, see if you need any additional clarification.Signorina
S
6

useMutation is a hook and it has state in it which cause a re-render

See the useMultation type bellow:

export interface MutationResult<TData = any> {
    data?: TData | null;
    error?: ApolloError;
    loading: boolean;
    called: boolean;
    client: ApolloClient<object>;
}

The loading state is the one causing re-render, so in which component you delcare useMutation it will re-render due to that state


One possible fix is to separate the component with useMutation to another one

Something like this:

function App() {
  return (
    <>
      <Header />  // <-- this coponent won't re-render
      <UpdateUserButton />  // <-- useMutation is in here!
    </>
  );
}

Checkout the live example:

Edit sweet-cohen-4w6ef

Signorina answered 6/9, 2021 at 3:57 Comment(5)
Oh, I see. Is there a way that I can stop Header from updating, though? I've heard about React.memo, etc. but I don't know how those work. In any case thanks for your answer!Fattish
loading is a state so I don't think that's possible. Separate components for specific actions is also a best practice to "separate of concerns".Signorina
You could vote or mark an answer as accept if you find it useful and highlight it for others who need.Signorina
Sorry, I know I can do that, I had to go after my comment. Thanks so much for your answer!Fattish
Thank you!, This means a lotSignorina
C
3

Well, if you don't need the data returned by the mutation, you can also pass the ignoreResults option. Data will not be returned from the mutation hook but loading would called neither which prevents a re-render.

https://www.apollographql.com/docs/react/data/mutations/#ignoreresults

const [someMutation] = useSomeMutation({
 ignoreResults: true,
});
Cartan answered 14/12, 2021 at 12:4 Comment(1)
This should be the most upvoted answerHewes
L
1

Following should work (not tested)

  • Since you don't want re-render, you don't need to use useMutation hook which has state.
  • Instead you can directly import you apollo-client and use mutate on it without using useMutation hook.
  • You can choose to use the result in .then function or get rid of it.
import { apolloClient } from '../yourApolloClientFile';


const App = () => {

  const myMutation = (vars) => apolloClient.mutate({
    mutation: gql`
        mutation Test($updateUserId: ID!, $updateUserInput: UpdateUserInput!) {
          updateUser(id: $updateUserId, input: $updateUserInput) {
            id
            firstname
            age
          }
        }
      `,
    variables: vars
  })
    .then(result => console.log(result))
    .catch(error => console.log(error))


  // Runs every time this component is rendered
  console.log("rendered");

  // Called when the button is clicked
  const update = () => {
    myMutation({
      updateUserId: 1,
      updateUserInput: {
        age: Math.round(Math.random() * 10 + 5) // Set a random age from 5 - 15
      }
    });
  };

  return (
    <>
      <h1>Update Test</h1>
      <button onClick={update}>Update!</button>
    </>
  );
};

Lophophore answered 20/12, 2021 at 5:0 Comment(1)
++ to the tip to do the mutation directly with apollo client (I used the useApolloClient hook) This means you can still access the data you need, while skipping the state update that rerenders your component.Barnes
S
0

This is likely because the mutation is returning a normalised object which this component or a higher component depends on via a hook like useQuery.

If you don't care about the result of the mutation make sure it doesn't return the normalised object.

Something like

mutation SomeMutation ($input: SomeMutationInput!){
  someMutation (input: $input){
    ok
  }
}
Stroud answered 5/9, 2021 at 12:25 Comment(2)
So... how do I fix it? If this is the case, how can I stop depending on it?Fattish
apollographql.com/docs/react/data/queries/… It seems like fetchPolicy="standby" should work, but it doesn't for meIvaivah

© 2022 - 2024 — McMap. All rights reserved.