2

When creating a record, an error appears in the console that:

"Warning: A store update was detected within another store update. Please make sure new store updates aren't being executed within an updater function for a different update"

The mutation works, but the updater and optimisticUpdater fails to add a node to the store. I need the app to update the UI when creating or deleting a record.

I can do it using Redux, but I would like to do it using the standard Relay tools. Here are the files:

Environment.js:

import {
  Environment,
  Network,
  QueryResponseCache,
  RecordSource,
  Store,
} from 'relay-runtime';
const oneMinute = 60 * 1000;
const cache = new QueryResponseCache({ size: 250, ttl: oneMinute });
function fetchQuery(
  operation,
  variables,
  cacheConfig,
) {
  const queryID = operation.text;
  const isMutation = operation.operationKind === 'mutation';
  const isQuery = operation.operationKind === 'query';
  const forceFetch = cacheConfig && cacheConfig.force;
  const fromCache = cache.get(queryID, variables);
  if (
    isQuery &&
    fromCache !== null &&
    !forceFetch
  ) {
    return fromCache;
  }
  return fetch(`${process.env.REACT_APP_API_PORT}graphql`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      query: operation.text,
      variables,
    }),
  }).then(response => {
    return response.json();
  }).then(json => {
    if (isQuery && json) {
      cache.set(queryID, variables, json);
    }
    if (isMutation) {
      cache.clear();
    }
    return json;
  });
}
const environment = new Environment({
  network: Network.create(fetchQuery),
  store: new Store(new RecordSource()),
});
export default environment;

CreateHeroMutation.js:

import { commitMutation, graphql } from 'react-relay';
import environment from '../Environment';
import { ConnectionHandler } from 'relay-runtime';
const mutation = graphql`
  mutation CreateHeroMutation($input: CreateHeroInput!) {
    createHero(input: $input) {
      hero {
        id
        name
        date
      }
    }
  }
`;
function sharedUpdater(store, viewer, newEdge) {
  const viewerProxy = store.get(viewer.id);
  const conn = ConnectionHandler.getConnection(
    viewerProxy,
    'HeroesList_viewer', 
  );
  ConnectionHandler.insertEdgeAfter(conn, newEdge);
}
let tempID = 0;
function CreateHeroMutation(viewer, name, date) {
  commitMutation(
    environment,
    {
      mutation,
      variables: {
        input: {
          name,
          date
        }
      },
      updater: (store) => {
        const payload = store.getRootField('createHero');
        const newEdge = payload.getLinkedRecord('hero');
        sharedUpdater(store, viewer, newEdge);
      },
      optimisticUpdater: (store) => {
        const id = 'client:newHero:' + tempID++;
        const node = store.create(id, 'Hero');
        node.setValue(name, 'name');
        node.setValue(id, 'id');
        const newEdge = store.create(
          'client:newEdge:' + tempID++,
          'hero',
        );
        newEdge.setLinkedRecord(node, 'node');
        sharedUpdater(store, viewer, newEdge);
      },
      onCompleted: (response, errors) => {
        console.log('Response received from server.');
      },
      onError: err => console.error(err),
    },
  );
}
export default CreateHeroMutation;

Also, the entire project is available here:

https://github.com/narzantaria/fullstack-relay-app

Thanks, regards.

Kiten
  • 985
  • 2
  • 17
  • 35

1 Answers1

0

One of the easiest ways of handling this kind of scenario in Relay is to return the entire list of items as a payload response and then in relay mutation query for that. This way Relay will update the store with new added/removed node automatically.

const CreateHeroMutation = mutationWithClientMutationId({
  name: "CreateHero",
  inputFields: {
    name: { type: new GraphQLNonNull(GraphQLString) },
    date: { type: new GraphQLNonNull(GraphQLString) }
  },
  outputFields: {
    hero: {
      type: Hero,
      resolve: obj => obj
    }
  },
  mutateAndGetPayload: args => {
    const hero = new heroModel({
      name: args.name,
      skills: [],
      date: new Date(args.date)
    });
    return hero
      .save()
      .then(result => {
        console.log(result);
        return {
          ...result._doc,
          id: result._doc._id.toString(),
          date: new Date(result.date).toISOString(),
          // here return a list of heroes
          heroes: 
              connectionFromPromisedArray(heroModel.find({}).limit(100).sort({ _id: -1 })
         .then(heroes => {
           return heroes.map(hero => {
              return {
                ...hero._doc,
                id: hero.id,
               date: new Date(hero.date).toLocaleDateString()
             };
          });
         })
        };
      })
      .catch(err => {
        console.log(err);
        throw err;
      });
  }
});

Then in front end you can query for the whole list and Relay will automatically update the store for you.

mutation CreateHeroMutation($input: CreateHeroInput!) {
  createHero(input: $input) { 
    heroes {
      edges {
        node {
          id
          ...HeroTpl_hero
        }
     }
   }
 }
`;
Matin Kajabadi
  • 3,414
  • 1
  • 17
  • 21