Relay: Relay modern Mutation changes in relay store but doesn't updates the UI

Created on 27 Apr 2017  Â·  50Comments  Â·  Source: facebook/relay

I don't know if it's an issue, I'm new to Relay.. After doing the mutation in relay modern,

 const mutation = graphql`
       mutation addPostMutation (
       $input: addPostData!
      ) {
           addPost(data: $input) {
           node {
              id
              content
       }
      }
     }
   `;

    commitMutation(
          environment,
          {
              mutation,
             variables,
              onCompleted: (res) => {
               console.log(environment)
               console.log(res)
        },
         onError: err => console.error(err)
      }
     )

when I console.log environment, I can see the newly added post in the relay store, but it's not updated in the UI. However, after refreshing the app, the changes are made. This is my schema.graphql

schema {
query: Root
mutation: Mutation
}

type Mutation {
addPost(data: addPostData!): addPostMutation
}

input addPostData {
userID: String,
post: String,
tags: [String]
}

type addPostMutation {
node: postNode
}

interface Node {
id: ID!
}

type postEdges {
node: postNode
}

type postNode implements Node {
id: ID!
content: String
}

Most helpful comment

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 :

const updater = (store) => {
        let rootField = store.getRootField(`createFundingSource`)
        store.getRoot().copyFieldsFrom(rootField)
}

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

All 50 comments

cc @kassens - I'm not sure how mutations interact with lists of values. I think since the "post" you added was not in the UI before the mutation that it does not update by default.

However it's also unclear from your example what's going on. For example, what uses the postEdges type? Where is your Root type?

schema {
query: Root
mutation: Mutation
}

type Root {
AllPosts: getPostsQuery
node(id: ID!): Node
}

type Mutation {
addPost(data: addPostData!): addPostMutation
}

interface Node {
id: ID!
}

input addPostData {
userID: String,
post: String,
tags: [String]
}

type addPostMutation {
node: postNode
}

type getPostsQuery {
edges: [postEdges]
}

type postEdges {
node: postNode
}

type postNode implements Node {
id: ID!
content: String
}

Yes of course it was not in the UI before. I want the list of post to update as I add the post. Though it only updates after I refresh the page.

@saudpunjwani101 I belive you need to use the updater in your mutation to place the new data into the AllPosts connection. Unforunatly there doesn't seem to be a lot of documentation right now on the API of the RecordSourceSelectorProxy, but you can view its flow typings here.

Yeah I guess but can't really figure it out how to update the store passed in the updater.

The response is normalized in the store, but it is not appended in getPostsQuery
Basically, you want something like this

const updater = proxyStore => {
  const newEdgeNode = proxyStore.getRootField('addPost'),
  // since 'AllPosts' is under Root
  const prevPosts = proxyStore.getRoot().getLinkedField('AllPosts');
  const prevEdgeNodes = prevPosts && prevPosts.getValue('edges');
  if (prevEdgeNodes) {
    prevEdgeNodes.push(newEdgeNode); // You might want to append or prepend
    prevPosts.setLinkedRecords(prevEdgeNodes, 'edges');
  }
}

@JenniferWang Thanks for the example! I'm a bit confused on how proxyStore.getRootField('addPost') == newEdgeNode. I guess it seems a little unintuitive that getRootField somehow pulls the result of a mutation?

ah, we have a pretty bad naming for this. I am not super sure if this is the case, but if you print out the store in onComplete callback(when the changes are committed to the store), you should be able to see a key for the top-level normalized record for the mutation payload under root -- since we need a systematic way to create a key for the payload, a natural choice is under root and that's why it's named as getRootField.

Thanks for the update...

proxyStore.getRoot().getLinkedField() is not a function. Can you refer to any docs?

Should be getLinkedRecord
This is a good place to lookup all the API on the proxy store/record
https://github.com/facebook/relay/blob/master/packages/relay-runtime/store/RelayStoreTypes.js#L150-L174

yeah right. What other approach can we use other than manually updating the store?

I'm trying to do the same thing and have been playing around with the updater function example (thanks @JenniferWang). I can't get it to work though. const prevEdgeNodes = prevPosts && prevPosts.getValue('edges'); gives this error:

Error: RelayModernRecord.getValue(): Expected a scalar (non-link) value for 'client:root:AllPosts.edges' but found plural linked records.

(I updated the naming in this example to follow @saudpunjwani101's schema. Obviously I have different fields and names in my schema).

Has anyone else come across this? I'm reading the source but haven't been able to figure out what I'm doing wrong yet.

@erikfrisk try doing const prevEdgeNodes = prevPosts && prevPosts.getLinkedRecords('edges'); it worked for me

@saudpunjwani101 Thanks! That got me a little further. I had the idea to try getLinkedRecord yesterday but not getLinkedRecords :)

Now I'm no longer getting errors in the console but the UI still isn't updating. Did you manage to get it to work all the way?

Yeah make sure you append the data.

I thought I was doing that. Here's my updater function. Can you tell what I'm doing wrong?

updater: (store) => {
  const newEdgeNode = store.getRootField('createReport');
  const prevReports = store.getRoot().getLinkedRecord('reports');
  const prevEdgeNodes = prevReports && prevReports.getLinkedRecords('edges');
  if (prevEdgeNodes) {
    prevEdgeNodes.unshift(newEdgeNode);
    prevReports.setLinkedRecords(prevEdgeNodes, 'edges');
  }
},

reports in my schema is basically the equivalent of AllPosts in your schema. The relevant parts of my schema:

input CreateReportInput {
  name: String!
  language: String
  clientMutationId: String
}

type CreateReportPayload {
  report: Report
  clientMutationId: String
}

type Mutation {
  createReport(input: CreateReportInput!): CreateReportPayload
}

# An object with an ID
interface Node {
  # The id of the object.
  id: ID!
}

# Information about pagination in a connection.
type PageInfo {
  # When paginating forwards, are there more items?
  hasNextPage: Boolean!

  # When paginating backwards, are there more items?
  hasPreviousPage: Boolean!

  # When paginating backwards, the cursor to continue.
  startCursor: String

  # When paginating forwards, the cursor to continue.
  endCursor: String
}

type Query {
  # Fetches an object given its ID
  node(
    # The ID of an object
    id: ID!
  ): Node
  reports(after: String, first: Int, before: String, last: Int): ReportConnection
}

type Report implements Node {
  # The ID of an object
  id: ID!
  mongoId: ID
  name: String
  language: String
}

# A connection to a list of items.
type ReportConnection {
  # Information to aid in pagination.
  pageInfo: PageInfo!

  # A list of edges.
  edges: [ReportEdge]
}

# An edge in a connection.
type ReportEdge {
  # The item at the end of the edge
  node: Report

  # A cursor for use in pagination
  cursor: String!
}

the code looks okay to me. Can u console.log the prevReports to see if the new node actually appeared in the store?

I think I'm starting to see the problem. I did what you said @saudpunjwani101 and noticed that the new node looked different than the others when i logged prevReports. I guess the updater function I'm using is grabbing the mutation payload and trying to add that as an edge on the report connection. The problem is it's not the right type. The mutation payload (newEdgeNode?) is this...

type CreateReportPayload {
  report: Report
  clientMutationId: String
}

... and prevEdgeNodes that I'm prepending to is an array of this...

type ReportEdge {
  # The item at the end of the edge
  node: Report

  # A cursor for use in pagination
  cursor: String!
}

Makes sense that it doesn't work. I can get the report itself from the mutation payload by doing newEdgeNode.getLinkedRecord('report'). Now I just need to figure out how to manually create a new ReportEdge in the updater function so that I can add the new report to the ReportEdge's node field and add the ReportEdge to the ReportConnection. Does anyone reading this know how to do that? 😄

@saudpunjwani101: I see that your mutation payload has a node field, which I guess makes it work in your case. Doesn't the mutation payload also need to have a clientMutationId field in order to be Relay compliant?

Also, this all seems pretty complex for a very common use case. Is this going to be simplified in future releases of Relay Modern or am I just finding it difficult because I'm a n00b?

Yes, this is not correct.

prevEdgeNodes.unshift(newEdgeNode);

It would be helpful if you could append your mutation here. Generally there will be three cases for a connection field.

  1. If it is in a pagination container, you must have supplied @connection(key: 'someKey'), then you should be able to use RelayConnectionHandler to delete the edge (if you know the node id)
  2. If it is fetched like this report(first: 3), then in your mutation query, you probably want to refetch the whole thing, relay will update the store automatically (you don't need to supply an updater function)
  3. If the connection field is fetched by different views with both 1 & 2, you should refetch the connection field AND give an updater function like in 1.

Thanks for the info @JenniferWang. Do you mean append the mutation as described by the schema?

input CreateReportInput {
  name: String!
  language: String
  clientMutationId: String
}

type CreateReportPayload {
  report: Report
  clientMutationId: String
}

type Mutation {
  createReport(input: CreateReportInput!): CreateReportPayload
}

(I also posted my full schema in a previous post.)

The mutation itself doesn't do anything special right now (I think):

import {commitMutation, graphql} from 'react-relay';
import environment from 'data/environment';

const mutation = graphql`
  mutation CreateReportMutation(
    $input: CreateReportInput!
  ) {
    createReport(input: $input) {
      report {
        id
        mongoId
        name
        language
      }
    }
  }
`;

function commit({name, language}) {
  commitMutation(
    environment,
    {
      mutation,
      variables: {
        input: {
          name,
          language,
        },
      },
      // updater: (store) => {
      //   // Do some magic that I haven't figured out yet. 
      // },
    },
  );
}

export default {commit};

Right now the reports are just fetched and shown in a component without taking advantage of the fact that they're in a connection. The fact that they're in a connection is more to make it future-proof as I build out the web app. This is my ReportList component (with some irrelevant bulk cut out):

import React, {Component} from 'react';
import {createFragmentContainer, graphql} from 'react-relay';
import RaisedButton from 'material-ui/RaisedButton';
import {Link} from 'react-router-dom';
import CreateReportMutation from 'mutations/CreateReportMutation';

class ReportList extends Component {

  addReport() {
    CreateReportMutation.commit({
      name: 'My Report',
      language: 'en',
    });
  }

  render() {
    const reports = this.props.reports.edges.map((edge) => 
      <li key={edge.node.id}>
        <Link to={`/reports/${edge.node.id}`}>
          {edge.node.name} ({edge.node.mongoId})
        </Link>
      </li>
    );
    return (
      <div>
        <RaisedButton 
          label="Add report"
          primary={true}
          onTouchTap={this.addReport}
        />
        <ul>
          {reports}
        </ul>
      </div>
    )
  }
}

export default createFragmentContainer(
  ReportList,
  graphql`
    fragment ReportList_reports on ReportConnection {
      edges {
        node {
          id
          mongoId
          name
        }
      }
    }
  `,
);

"Refetching the whole thing" sounds good. How do I do that in Relay Modern? The only thing I can find in the documentation is the RefetchContainer, but that seems to be meant for more advanced situations, right?

@erikfrisk I believe I was running into the same issue you are/were experiencing – adding a new edge to a connection. This example helped me get there:

https://github.com/relayjs/relay-examples/blob/442903989d04e3e76363880e6ebf3cfc74f25918/todo-modern/js/mutations/AddTodoMutation.js#L39-L46

The secret sauce (as Jennifer alluded too) was to import {ConnectionHandler} from 'relay-runtime' and use it as the example demonstrates.

I also had to restructure my fragment some using this related example as a guide:

https://github.com/relayjs/relay-examples/blob/442903989d04e3e76363880e6ebf3cfc74f25918/todo-modern/js/components/TodoList.js#L63-L83

Thanks @cipater! That, plus reading the RelayConnectionHandler source, gave me the last bits I needed. It works now! 😄

Posting my solution here for others. This is my updater function:

import {ConnectionHandler} from 'relay-runtime';

...

updater: (store) => {
  const payload = store.getRootField('createReport');
  const newReport = payload.getLinkedRecord('report');
  const storeRoot = store.getRoot();
  const connection = ConnectionHandler.getConnection(
    storeRoot,
    'ReportList_reports',
  );
  const newEdge = ConnectionHandler.createEdge(
    store,
    connection,
    newReport,
    'ReportEdge',
  );
  ConnectionHandler.insertEdgeBefore(connection, newEdge);
},

The fragment in my ReportList component:

export default createFragmentContainer(
  ReportList,
  graphql`
    fragment ReportList_query on Query {
      reports(
        first: 2147483647 # max GraphQLInt
      ) @connection(key: "ReportList_reports") {
        edges {
          node {
            id
            mongoId
            name
          }
        }
      }
    }
  `,
);

@JenniferWang how do u refetch the whole thing? is there like a props.refetch method in relay like Apollo client?

@saudpunjwani101 use the RefetchContainer?
https://facebook.github.io/relay/docs/refetch-container.html

@saudpunjwani101 A common practice is to have a structure such as:

RefetchContainer // to refetch the connection in-full when filter arguments change
  PaginationContainer // to fetch more items with the same filter arguments
    Array<FragmentContainer> // to render each edge

hmm, I don't think @saudpunjwani101 is asking about pagination container.
What I mean by "refetch" is that you could refetch the connections in the mutation payload. However, this involves changes in your current schema. I'm not sure how to modify your schema, but we usually have

type Query {
  # ...
  viewer: Viewer
}

type Viewer {
  # ...
  reports(after: String, first: Int, before: String, last: Int): ReportConnection
}

type Mutation {
  createReport(input: CreateReportInput!): CreateReportPayload
}

type CreateReportPayload {
  report: Report 
  clientMutationId: String
  viewer: Viewer
}

Then the query fragment will be

  graphql`
    fragment ReportList_viewer on Viewer {
      reports(
        first: 20 # I don't think a max int makes sense 
      ) {
        edges {
          node {
            id
            mongoId
            name
          }
        }
      }
    }
  `,

And the mutation will be

  mutation CreateReportMutation(
    $input: CreateReportInput!
  ) {
    createReport(input: $input) {
      viewer {
       ...ReportList_viewer # You could use fragment spread here
      }
    }
  }

Then there is no need to have your updater function. The one downside of this is that it will be cumbersome to provide an optimistic response.

In your case, if you really want to fetch many edges in the query, then it makes sense to use @connection and give a customized updater function because it will be too costly to fetch everything again in the mutation. In that case, you could just fetch the newly created edge and use helper functions in RelayConnectionHandler to update the client side (and this is exactly what you are doing now).

yeah that answers to it

@JenniferWang how do i get the viewer object in Root Query.
PS the viewer object does not have a unique ID.

store.getRoot().getLinkedRecord('viewer') does not work

my structure is like this:

query{
  viewer{
    user{ id firstName LastName}
    feed1{ edges{node{id}}} @connection(key: 'someKey')
    feed2{ edges{node{id}}} @connection(key: 'someKey2')
  }
}

i need to get the store to update someKey

@stoffern I'm not sure whether you are using RelayViewerHandler. If you use that, the viewer object could be accessed as a node

store.get(VIEWER_ID)

https://github.com/facebook/relay/blob/634fca46c036ec34301c627e7f766092b9c66daa/packages/relay-runtime/handlers/viewer/RelayViewerHandler.js#L68

If you are not using it, I think store.getRoot().getLinkedRecord('viewer') should have worked? You could also dump the store proxy by the following code to see what does the store look like at that point. It is also possible that you haven't fetched a query that ask for viewer yet.

const updater = (store) => {
  require('RelayStoreProxyDebugger'). dump(store);
}

Neighter work.
I have tried and the store is:
client:root:viewer:__Feeds_feedClosest_connection{}

__Update__
It is my args that are the issue. Seems i have a problem with dynamic args.
created a separate issue here: https://github.com/facebook/relay/issues/1764

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 :

const updater = (store) => {
        let rootField = store.getRootField(`createFundingSource`)
        store.getRoot().copyFieldsFrom(rootField)
}

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

I had a similar problem: I needed to update specific query when mutation was committed. I had this query in my header component

Person(where: $where) {
    id
    title
    firstName
    middleName
    lastName
    gender
  }

where = { id: { eq: '7' } }

So in relay store there was record Person{"where":{"id":{"eq":"7"}}} and it was null.

In my mutations config in updater function i wrote this:

(store) => 
    const record = store.get(store.getRootField('login').getLinkedRecord('user').getValue('id'));
    store.getRoot().setLinkedRecords([record], 'Person', { where: { id: { eq: '7' } } })

my mutations response looks like this: { login: { user: { id: .....} } }

I know it's very specific case but now it's possible to work with it further. Basically the idea is to get whatever comes from the server find the needed record in the relay store and just reset it.

Or if you have MeQuery query all you need to do to update it in relay store is store.getRoot().setLinkedRecord(record, 'MeQuery');

I got struck with this problem where it didn't update the UI in React-native App but work in Web.
Thank you @erikfrisk for saving my time not to move to Apollo.

Hi, I am running into the same issue here (relay 1.4.0). I'm using the viewer pattern, but I am NOT using connections and pagination as I don't require that at the moment.

The mutation I am performing gets correctly updated into the relay store, and is correctly inserting records through my updater function, but Relay never automatically updates the UI.

My work around at the moment is to run a completed callback which runs setState on the component to manually update the UI. I am happy with that for now. But it would be nice to know how to get Relay to update the UI after a mutation is successfully performed?

For anyone interested here is how my updater and onCompleted function looks like. is there anything I am missing here?

Also I am using found-relay for my routing. I haven't heard this mention by anyone. But wondering if this is an element tat could be affecting this issue as well?


Example graphQL mutation input:

mutation CreateTimerMutation($input: CreateTimerInput!){
  createTimer(input: $input) {
    createdTimer{
      id, 
      name, 
      parentTimerId
    }
  }
}

Example graphQL mutation payload:

{
  "data": {
    "createTimer": {
      "createdTimer": {
        "id": "534bec16-42c6-4e09-806c-10a2ac5680f8",
        "name": "New timer 3",
        "parentTimerId": null
      }
    }
  }
}

Updater and onCompleted mutation configs:

    updater(store) {
      const payloadProxy = store.getRootField('createTimer');
      const createdTimerProxy = payloadProxy.getLinkedRecord('createdTimer');

      const viewerProxy = store.getRoot().getLinkedRecord('viewer');
      const timersProxy = viewerProxy.getLinkedRecords('timers');

      if(timersProxy){
        timersProxy.push(createdTimerProxy)
        viewerProxy.setLinkedRecords(timersProxy, 'timers')
      }
    },
    onCompleted(response, errors) {
      //this runs setState on the component with the update data from viewer
      //I'd rather Relay update the UI automatically
      completed() 
    }

Schema: here's the related schema with relevant sections included:

interface Node {
  id: ID!
}

schema {
  query: Query
  mutation: Mutation
}

type Query { 
  viewer: User
}

type Mutation {
  createTimer(input: CreateTimerInput!): CreateTimerMutationPayload
}

type User implements Node {
  id: ID!
  timers: [Timer!]!
}

type Timer implements Node {
  id: ID!
  parentTimerId: ID
  name: String
...
}

input CreateTimerInput {
  parentTimerId: ID
  name: String
...
  clientMutationId: String
}

type CreateTimerMutationPayload {
  createdTimer: Timer
  clientMutationId: String
}

My query on the component

``` export const AdminTimersQuery = graphql
query adminTimers_Query {
viewer {
id
...adminTimers_viewer
}
}
`;

````

and the related fragment...

``` export default withRouter(createFragmentContainer( AdminTimers, graphql
fragment adminTimers_viewer on User {
timers {
id,
name,
parentTimerId
}
}
`));

````

Hi, i am having the same issue of relay store getting updated but my UI is not updating. Its getting updated if i refresh the page.

This is my CreateChatMutation file where i am running createChat mutation and rendering the data using QueryRenderer:

import {commitMutation, graphql} from 'react-relay'
import {ConnectionHandler} from 'relay-runtime'
import environment from './Environment'
import LinkList from './LinkList'
// import storeDebugger from 'relay-runtime/lib/RelayStoreProxyDebugger'

const mutation = graphql`
mutation CreateChatMutation($input: CreateChatInput!){
  createChat(input: $input){
    chat{
      id
      from
      content
      createdAt
    }
  }
}`

let temp =0
export default (from, content, callback) => {
  const variables = {
    input: {
      from,
      content,
      clientMutationId:""
    },
  }

  commitMutation(
    environment,{
      mutation,
      variables,

      onCompleted: (response)=>{
        console.log('response: ', response);
        const id = response.createChat.chat.id;
        callback(response);
},

      updater: proxyStore => {
//
//           // storeDebugger.dump(proxyStore)
//
        const payload = proxyStore.getRootField('createChat');
        console.log('updater: payload : ',payload);
        const newReport = payload.getLinkedRecord('chat');
        console.log('updater: newReport : ',newReport);
        const storeRoot = proxyStore.getRoot();
        console.log('updater: storeRootss : ',storeRoot);
        // storeRoot.setValue(payload.getLinkedRecord('chat').getValue('from'), from);
        // storeRoot.setLinkedRecord()
        // console.log('updater: storeRootinggg : ',storeRoot);
        const connection = ConnectionHandler.getConnection(
          payload,
          'LinkList_allChats'
        );
        console.log('updater: connection : ',connection);
        const newEdge = ConnectionHandler.createEdge(
          proxyStore,
          connection,
          payload,
          'ChatEdge'
        );
        console.log('updater: newEdge : ',newEdge);
        ConnectionHandler.insertEdgeBefore(connection, newEdge);

},
  // console.log('one:',prevPosts)

      onError: err => console.error(err),
    },
  )
}

This is my LinkList file which i am using in CreateChatMutation:

import React, {Component} from 'react'
import Link from './Link'
import {createFragmentContainer, graphql} from 'react-relay'

class LinkList extends Component{
  render() {
    return(
      <div>
      {
        this.props.viewer.allChats.edges.map(({node}) =>
        <Link key={node.__id} link={node}/>
      )}
        </div>
    )
  }
}

export default createFragmentContainer(LinkList, graphql`
fragment LinkList_viewer on Viewer{
  allChats(last: 100, orderBy: createdAt_ASC) @connection(key:"LinkList_allChats", filters:[]){
    edges{
      node{
        ...Link_link
      }
    }
  }
}`)

Any help would be appreciated. Thanks

we have this helpers to update connections

export function connectionUpdater({ store, parentId, connectionName, edge, before, filters }) {
  if (edge) {
    if (!parentId) {
      // eslint-disable-next-line
      console.log('maybe you forgot to pass a parentId: ');
      return;
    }

    const parentProxy = store.get(parentId);

    const connection = ConnectionHandler.getConnection(parentProxy, connectionName, filters);

    if (!connection) {
      // eslint-disable-next-line
      console.log('maybe this connection is not in relay store yet:', connectionName);
      return;
    }

    if (before) {
      ConnectionHandler.insertEdgeBefore(connection, edge);
    } else {
      ConnectionHandler.insertEdgeAfter(connection, edge);
    }
  }
}

Usage:

const newEdge = store.getRootField('createChat').getLinkedRecord('chatEdge');

connectionUpdater({
        store,
        parentId: viewer.id,
        connectionName: 'LinkList_allChats',
        edge: newEdge,
        before: true,
      });

Returning an edge from mutation makes things easier, but you could also create the edge on client

Thanks for that, but what is viewer.id?. Please don't mind me asking simple questions as i am completely new to react and relay. You can look at the viewer code that i have put above in LinkList class

Can anyone please put some light on (viewer.id). I am stuck from last 4 days.

Viewer.id is the global id of your graphql query. It should be the root of
all your query objects.

On Tue, 5 Feb 2019 at 10:35 PM, Prashanth Verma notifications@github.com
wrote:

Can anyone please put some light on (viewer.id). I am stuck from last 4
days.

—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
https://github.com/facebook/relay/issues/1701#issuecomment-460683383,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ASVaNS2SBrf-Vx9Ek16FYYfbRhOzTMVfks5vKaTcgaJpZM4NKLAX
.

fragment LinkList_viewer on Viewer {
   id
   allChats 
}

You should avoid viewer anyway, it a old and bad pattern, used only on relay classic

When using fragments on QueryType, the parent of the connection is ROOT_ID

import { ROOT_ID } from 'relay-runtime';

So I import ROOT_ID from relay-runtime and can pass ROOT_ID instead of viewer.id?

only if you have something like this

fragment LinkList_query on Query {
   allChats(...) @connection
}

You should avoid viewer anyway, it a old and bad pattern, used only on relay classic

@sibelius Just out of curiosity - why is it a bad pattern? (I never actually used it) I always thought it's quite nice to have this extra level where you can deal with all the auth and other _viewer_ specific issues (for example whitelabel version so you don't have to send it in headers or to every query).

Use me field to represent logged user, passing auth token on header makes more sense, so u have one less variables in all queries and mutation

I'm having the same issue with QueryRenderer on React Native re-rendering with outdated props after the mutation updater has been called, and the store has been updated properly.

Note: I'm not using connections – could it be that QueryRenderer is re-fetching when new edges/nodes are connected?

// Schema extract
User {
  id: ID!
  data: [ Data! ]!
} 

mutation {
  store(input: DataCreateManyInput!): User!
}

// Updater function
const payload = store.getRootField('store')                                                                              
const userProxy = store.get(payload.getValue('id'))
const data = payload.getLinkedRecords('data')                                                                           
userProxy.setLinkedRecords(data, 'data')

// React fragment container
const DataFragmentContainer = createFragmentContainer(
    Data,
    graphql`
        fragment data_user on User {
            id
            data(orderBy: date_ASC) {
                type
                unit
                date
                value
                user {
                    id
                }
            }
        }
  `,
)

export default ({ navigation }) => {
    return (
        <QueryRenderer
            environment={Environment}
            query={graphql`
                query dataQuery {
                    viewer {
                        ...data_user
                    }
                }
            `}
            render={({ error, props }) => {
                let viewer = props && props.viewer
                return <DataFragmentContainer user={viewer} navigation={navigation} />
            }}
        />
    )
}

check if your QR is not re-rendering

I think navigation prop change and QR will rerender with stale data

The QR is re-rendering, but with stale data indeed. Is there a workaround?

avoid rerender QR

Hello, I was having the same problem.. but @JenniferWang helped me a lot!!

Here is what I was trying to do... I have two input fields to register a user and a list to show the users, when I insert a new user I wanted to display the just inserted user in that list. Here's my (and @JenniferWang) solution if anyone is having any problems..

My QueryRenderer

<QueryRenderer
            environment={environment}
            query={query}
            render={(result: any) => {
                const users =
                    result.props === null ? null : result.props.PedroBotelho
                return (
                    <div className="App-header">
                        <Form users={users}/>
                        <List users={users}/>
                    </div>
                )
            }}
        />

My commitMutation

commitMutation(environment, {
            mutation: mutation,
            variables: {
                name: values.name,
                lastName: values.lastName,
            },
            updater: (store: any, data: any) => {
                **const prevPosts = store.getRoot().getLinkedRecords('PedroBotelho')
                prevPosts.push(store.getRootField('PedroBotelhoAdd'))
                store.getRoot().setLinkedRecords(prevPosts, 'PedroBotelho')**
            }
        })

PedroBotelho is the name of my graphql query and PedroBotelhoAdd is my mutation.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MartinDawson picture MartinDawson  Â·  3Comments

brad-decker picture brad-decker  Â·  3Comments

sibelius picture sibelius  Â·  3Comments

derekdowling picture derekdowling  Â·  3Comments

bondanherumurti picture bondanherumurti  Â·  3Comments