relay: [Modern 1.1.0] Top-level object is not populated by Relay after upgrade to 1.1.0

In Relay 1.0.0 our code works correctly to fill the top-level userData object:

userData: {
  "id": "VXNlckRhdGE6MQ==",
  "waistSize": 0,
  "treatTotalForWeek": 0,
  "workoutDefinitions": {
    "edges": [
      {
        "node": {
          "id": "V29ya291dERlZmluaXRpb246MQ==",
          "description": "Push-Up Lowers (Wall)",
          "category": null
        }
      },
...

However, upgrading to 1.1.0 and making no other changes, we now get this (none of the data is filled). Relay is properly fetching everything from the server as we can verify with Wireshark, it’s just not populating the props:

userData: {
  "__fragments": {
    "TreatList_userData": {}
  },
  "__id": "VXNlckRhdGE6MQ=="
}

The relevant code looks like:

<QueryRenderer
  environment={environment}
  query={graphql`
    query TreatListQuery(
      $dateStrOfEntries: String!
      $countOfTreatsToDisplay: Int!
      $countOfWorkoutsToDisplay: Int!
      $firstDateOfWeek: String!
      $lastDateOfWeek: String!
    ) {
      userData {
        ...TreatList_userData
      }
    }
  `}
  ...
  render={({error, props}: any) => {
      return <TreatListContainer userData={props.userData}/>;
  }

@josephsavona @leebyron @wincent possible this is related to https://github.com/facebook/relay/commit/daf38f24b861a76c6acfbd3217e3043a699af991 as we have a top-level container object which is not named Viewer? I’ve combed through the published breaking changes and can’t find anything relevant.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 3
  • Comments: 24 (3 by maintainers)

Most upvoted comments

It’s a large container, I’ll try to add what’s relevant:

export default class extends Component <any, any> {
  public render(): JSX.Element { return(
    <QueryRenderer
      environment={environment}
      query={graphql`
        query TreatListQuery(
          $dateStrOfEntries: String!
          $countOfTreatsToDisplay: Int!
          $countOfWorkoutsToDisplay: Int!
          $firstDateOfWeek: String!
          $lastDateOfWeek: String!
        ) {
          userData {
            ...TreatList_userData
          }
        }
      `}
      variables={{...
      render={({error, props}: any) => {
          return <TreatListContainer userData={props.userData}/>;
      }}
// ...

class TreatList extends Component <ITreatListProps, ITreatListState> {

  private readonly _pickerWaistList = range(28, 40.001, 0.125);
  private readonly _notSpecifiedStr = "Not specified";

  private _newTreatAmountTextInput: any;
  private _newTreatDescriptionTextInput: any;
  private _repetitionsTextInput: any;
  private _workoutNotesTextInput: any;

  constructor() {
    super();
    this.state = {
      newDescriptionText: "",
      newAmountText: "",
      newWorkoutNotes: "",
      newWorkoutRepetitions: "",
      selectedWorkoutDefinition: "",
      dateStrOfEntries: Tools.standardStringFromDate(new Date()),
   } as ITreatListState;
  }

  public render(): JSX.Element {
    // **Logging userData here shows it's perfectly filled in 1.0.0;**
    // **In 1.1.0 it only contains the "fragment pointer"**
    console.log("userData TreatList: "
      + JSON.stringify(this.props.userData, null, 2));
    ...
const TreatListContainer = createRefetchContainer(
  TreatList,
  {
    userData: graphql`
      fragment TreatList_userData on UserData {
        id
        waistSize(date: $dateStrOfEntries)
        treatTotalForWeek: treatTotal(
          firstDate: $firstDateOfWeek,
          lastDate: $lastDateOfWeek,
        )
        workoutDefinitions(first: 2147483647) {
          edges {
            node {
              id
              description
              category
            }
          }
        }
        workoutEntries(
          first: $countOfWorkoutsToDisplay,
          dateOfEntries: $dateStrOfEntries,
        ) {
          edges {
            node {
              id
              category
              description
              notes
              countOfRepetitions
            }
          }
        }
        treats(
          first: $countOfTreatsToDisplay,
          dateOfEntries: $dateStrOfEntries,
        ) {
          edges {
            node {
              id
              description
              amount
            }
          }
        }
      }
    `,
  graphql`
    query TreatListRefetchQuery(
      $dateStrOfEntries: String!
      $countOfTreatsToDisplay: Int!
      $countOfWorkoutsToDisplay: Int!
      $firstDateOfWeek: String!
      $lastDateOfWeek: String!
    ) {
      userData {
        ...TreatList_userData
      }
    }
  `,
);

@alangenfeld I debugged this, thanks for the links - the issue was that our installed relay-runtime version was stuck at 1.0.0 and did not match react-relay version.

Fixed by adding an explicit relay-runtime dependency in package.json - however, that shouldn’t be necessary and I’m missing something here. Why would upgrading Relay via “yarn add react-relay@^1.1.0” not also update the dependent projects in node_modules?

I would have thought that simply updating the Relay package with Yarn wouldn’t break anything but it looks like there’s some extra step that’s either hidden or unknown to us.

Hmm you should only be able to access the data defined by the fragment TreatList_userData in its associated fragment container. The data structure with __fragments and __id ie the fragment pointer will allow the correct fragment container to get the data described in its fragment out of the store.

https://facebook.github.io/relay/docs/thinking-in-relay.html#data-masking

I think we can close this

if anybody hits this again

check this line of code

https://github.com/sibelius/ReactNavigationRelayModern/pull/18/files#diff-1d267d22ab1e8adae22aeaa0bc9dd136R64

This same issue @lukecwilliams describes happened to me when I upgraded to 1.1. I initially only upgraded react-relay and had the same problem - the fetched data was correct, but props were not populated.

To research this issue, I upgraded the relay-examples repository and ran into this issue again before resolving it via yarn upgrade-interactive and then selecting react-relay, relay-compiler and babel-plugin-relay.

I believe I was wrestling with something similar. I was finally able to get my components working even without a top-level object (i.e., no ‘viewer’ object). One thing to remember is that due to data masking, you can only see requested properties in the container that requested them. In other words, if you have a child container that declares a given fragment, the properties defined in that fragment will be visible only in that child component. They won’t be visible in any other child component nor will they be visible in the parent component (where you’d typically have your QueryRenderer). So if you’re debugging in the your parent component and you don’t see the properties for child component, this is due to data masking. Put more concretely:

If you have a child component whose fragment asks for property “foo” and you incorporate that fragment into the root query that is passed to the QueryRenderer, this.props.foo will be undefined in the QueryRenderer (parent component). So you’ll need to pass this.props (not this.props.foo) to the child component and in the child component you’ll be able to see this.props.foo.

In my case I hadn’t read the post about data masking (https://facebook.github.io/relay/docs/thinking-in-relay.html#data-masking) and even after reading it it wasn’t clear to me that the data was masked even in the parent component.

See the comments at the end of #1928