Relay: [Modern] How to use updater and optimisticUpdater

Created on 20 Apr 2017  路  17Comments  路  Source: facebook/relay

I'm trying to figure out the new cache update mechanisms. In trying to do so, I've hit a few major hurdles (mainly because it's not documented yet).

This is how I got things working:

commitMutation(environment, {
  mutation: graphql`
    mutation AppAddTagMutation($input: AddTagInput!) {
        addTag(input: $input) { tags }
    }
    `,
  variables: {
    input: { tag },
  },
  onCompleted: response => console.log(response),
  onError: error => console.error(error),
  updater: store =>
    store.getRoot().copyFieldsFrom(store.getRootField('addTag')),
  optimisticUpdater: store => {
    store
      .getRoot()
      ._mutator.setValue(store.getRoot()._dataID, 'tags', [
        ...tags,
        tag + '<--- optimistic',
      ]);
  },
});

When trying to use the store like this:
store.getRoot().setValue([...tags, tag], 'tags'), Relay complains with the following invariant: https://github.com/facebook/relay/blob/fcfe1b8496702ff29116445ee2c7b8453377159e/packages/relay-runtime/mutations/RelayRecordProxy.js#L70

Further, trying to poke around, it's not apparent how to get to the response data from within the updater or optimisticUpdater.

Another note - if I add a optimisticResponse, but no optimisticUpdater, the UI isn't updated until I receive data from the server. If I add the optimisticUpdater, the optimisticResponse becomes kind of pointless. I'd expect the optimisticResponse to run through the same updater, if no optimisticUpdater is provided.

Also, I'd expect the same code to work for both updater and optimisticUpdater. Yet when I have

optimisticUpdater: store => store.getRoot().copyFieldsFrom(store.getRootField('addTag'));

it produces an error, with and without the optimisticResponse.

Most helpful comment

Any updates on this issue? I'm having a hard time figuring out how to use the Store object from just looking at source.

All 17 comments

@josephsavona is there any documentation on this to be added? I'm trying the api around to see if i can update, but i'm not able to.

The issue with the example I'm running into when trying to replicate the sharedUpdater method is that the newEdge is a LinkedRecord holding onto a reference between calls, probably since it's being cached. Every time I insert a new edge, it basically adds a record and updates all previous records to match the values of the most recent Create. Is this intended and if so, is the pattern to destroy the Create Component that wraps the mutations each time we create a new record?

`function sharedUpdater(store, user, newEdge) {

const userProxy = store.get(user.id);
const conn = ConnectionHandler.getConnection(
userProxy,
'TodoList_todos',
);
ConnectionHandler.insertEdgeAfter(conn, newEdge);

}`

Just giving this a bump as we are having the same problem.

There is no clarity on how to update your UI on a Mutation. The only source of information we have is from GitHub.

We do not use an edge/node schema in the same way that the Facebook Todo example does, thus making it incredibly difficult to find any indication of the way to update our store and UI with a successful mutation.

Most of all, it isn't clear why the UI doesn't refresh with the store changes using :

 let rootField = store.getRootField(`createFundingSource`)
        store.getRoot().copyFieldsFrom(rootField)

Would be good to have some clearer documentation on the mutations and updaters.

My record is updated in the RecordSource sink, but never gets moved to base...

Any updates on this issue? I'm having a hard time figuring out how to use the Store object from just looking at source.

While I don't have an update (since I'm also waiting on the documentation, and desperately searching through the source code from time to time) I do have a link :-)

Check out the mutations in the example TODO app, which has been updated to use relay modern. While this certainly hasn't answered all of my questions, it's been an immensely helpful start.

I have written a blog post on some basic reading and writing https://medium.com/@tamis.mike/relay-mutation-updater-part-1-basic-reading-and-editing-c2789c111c75?source=linkShare-1a0179626ad-1504070174 hopefully this will help with your issue, but if it doesn't I can edit it or create a follow up blog post

@miketamis Seen as React relay modern documentation are horrific for mutations and I still don't get it I appreciate you writing this but I have some questions.

  • Why do we even need the updater function? Can't relay modern just automatically update our values?
  • Where do we get this node id from? Nobody explains this...
  • I'm not using Nodes or Edges. Can I update the values in the store anyway?

For example here's my file:

const mutation = graphql`
  mutation loginAccountMutation(
    $input: LoginAccountInput!
  ) {
    login(input: $input) {
      user {
        userName
      }
    }
  }
`;

export default ({ username, password }) => {
  const variables = {
    input: {
      username,
      password,
    },
  };

  commitMutation(environment, {
    mutation,
    variables,
    updater: (proxyStore) => {debugger
      const login = proxyStore.getRootField('login');
      const user = login.getLinkedRecord('user');
      const clientMutationId = user.getValue('clientMutationId');
      const userName = user.getValue('userName');

    },
  });
};

I then have a query somewhere else that uses this:

  fragment authorizedListContainer_user on ApplicationUser {
    userName
  }
  query primaryLayoutContainerQuery {
    user {
      ...authorizedListContainer_user,
    }
  }

I'v managed to get the username easily from the payload but how do I update the username in the store?

So many questions and 0 documentation and yet everyone somehow loves this library.

@MartinDawson if you only want to update a value and not a connection you don't need a updater function or config. Relay will do that for you. https://github.com/facebook/relay/issues/2157 and especially https://github.com/valorize/sm-chat/tree/master/src/mutations might help you.

@valorize hmm thanks but it still doesn't update automatically.

Do you have a repo where you could show your most recent code? Could it be that you need to fetch the id (chatmessage { id } in my case) so relay knows which node to update?

I had the same problem. I wanted to update a few fields on a root field (the viewer). The problem was, that my root field didn't have an id. I just added a constant id to the viewer and Relay handled updating the store automatically without any updater needed. Compare my repository and an official example

@jkettmann This was it. My payload was null when it was first accessed (on page load) because the user wasn't logged in yet, therefore the ID is also null. If I manually hardcode the id's then the values update automatically but obviously I can't hardcode, I think I have to use updater.

Here's what I got so far:

    const mutation = graphql`
      mutation loginMutation(
        $input: LoginInput!
      ) {
        login(input: $input) {
          user {
            id,
            userName
          }
        }
      }
    `; 
    updater: (store) => {
      const login = store.getRootField('login');
      const user = login.getLinkedRecord('user');
      const userName = user.getValue('userName');
      const id = user.getValue('id');

      const newUser = store.get(id);
      newUser.setValue(userName, 'userName');
    },

Just need to figure out why it doesn't update now with this updater :).

@MartinDawson How are you accessing the user in your application? My guess is that you have a root field on the Query type (or maybe viewer) that returns the logged in user. If that user is initially null, then there is no id to update in the mutation. Your updater should probably look something like:

    updater: (store) => {
      const login = store.getRootField('login');
      const loginUser = login.getLinkedRecord('user');
      const root = store.getRoot(); // the object corresponding to the root of a query
      root.setLinkedRecord(loginUser, 'user'); // assuming that user is accessible via `query { user }`
    },

This mutation is creating a new object, and Relay doesn't automatically know what existing parts of the graph should change to point to this new object - setLinkedRecord() does that.

@josephsavona Thanks, that makes sense now. That was it.

cc @jkettmann

Was this page helpful?
0 / 5 - 0 ratings