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.
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
@alangenfeld I didn't know the term "fragment pointer," that's helpful (though not in the docs as far as I can see).
We're still no closer to understanding why this breaks when we try to upgrade to 1.1.0, however. Again, in Relay 1.0.0 all works perfectly - the fragment data is available to the correct component as you note should be the case. In 1.1.0 however, the component only has __fragments, __id available.
Can you add the code for the fragment container in question (and possibly any intermediate containers)? Hopefully more information will point us in the right direction of what might have changed in 1.1.
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
}
}
`,
);
My $0.02: I was seeing something similar to this (the same error message about expecting a prop to receive data but it was null instead) in my code, but after I rearranged my GraphQL schema to have a top-level viewer field and then rearranged all of my queries to start at viewer instead, things started working for me.
@aergonaut Are you saying that you think that you must have a top-level field named "viewer" for this to work?
There is some code in Relay specifically to work with a top-level object named "viewer," but my impression is that it should work for any top level object name and not be tied to anything named "viewer" in the v1 world.
Ya the Viewer limitations should be removed in modern. Just to clarify you are using the modern environment and not compat right?
Sorry for no updates, I haven't been able to repro this issue on my side (or see it manifest anywhere @ FB).
@alangenfeld Thanks for the response. We're using the full Modern environment, yes. Two questions that will help us if you can answer:
Can you give us guidance on where or how to debug, to determine why the "fragment pointers" are being passed down to the child component as-is rather than being translated into usable data?
My impression was that Relay Modern is intended to be agnostic about what you name your top-level objects. If that's the case, why is there still code within Relay Modern that specifically work only on a top-level object that's named "viewer"? That different behavior makes me think we may indeed have problems b/c we're naming top level object userData instead of the conventionalviewer.
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
@blevine Thank you - our issue is that the data is not populated at all in the child where we expect it to be, after being successfully fetched. Note that as described, our implementation works perfectly in Relay 1.0.0, but not Relay 1.1.0.
@alangenfeld Would really appreciate a pointer to where we can debug, i.e. where is the code that is supposed to turn the props.userData fragment pointer into props.userData actual data from the GraphQL query. Have verified this multiple times - upgrading to Relay 1.1.0 with no other changes just breaks the app (data properly fetched by Relay but not filled). We'd like to not be using Relay 1.0.0 forever!
@leebyron Lee, we're pretty stuck here - Relay 1.1.0 breaks our app, going back fixes it. Would really appreciate even a brief look! Our top level userData object goes from containing the proper data, to instead containing __fragments.
Or would you let me know briefly where in the Relay code the props should be translated from a __fragment to proper data, so we can debug there and see why it's not? Thanks for your consideration.
upgrading to Relay 1.1.0 with no other changes just breaks the app
Did you rerun the build scripts? There may be changes to the generated .graphql.js files at play.
You can dig in here https://github.com/facebook/relay/blob/master/packages/react-relay/modern/ReactRelayFragmentContainer.js#L80-L123 for fragment containers or https://github.com/facebook/relay/blob/master/packages/react-relay/modern/ReactRelayRefetchContainer.js#L97-L143 for refetch containers (or nearby) to see whats happening for the reading out the fragment data.
edit: link to refetch containers as well
@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.
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.
@BenBrostoff yarn upgrade-interactive is useful, thanks. However when I update other libs I don't need to do anything more complex than yarn add some-package@^1.0.0. It would be very useful to know why this did not work with Relay.
(From the thumbs up on this issue I don't think we're the only ones who have hit it.)
@alangenfeld Can you help us out with what we're missing in the upgrade process? Just doing yarn add relay@^1.1.0 does not update the dependent relay-runtime, which caused this difficult-to-debug error for us. Others may well have this issue too.
We should probably add a peer dependency on react-relay to require the correct runtime and compiler versions.
@josephsavona Thanks Joe - we can keep them in sync locally, but I think your suggestion will avoid a lot of aggregate upgrade pain.
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
@sibelius What about the suggestion to add a peer dependency on react-relay as suggested by @josephsavona ?
it is already done on react-relay 1.2.0 or 1.3.0
@sibelius Great. Thanks!
@sibelius sweet
Most helpful comment
It's a large container, I'll try to add what's relevant: