Role of QueryRenderer in Relay Modern?
Asked Answered
U

2

19

So, first a bit of background. I'm a native iOS/Android developer who is now starting my first ever React Native project. It comes with all the benefits and pains of Javascript, but I like it a lot so far :-) I decided to also try my hand at GraphQL for the first time as well.

Being new to the React milieu in general, I also don’t have any prior knowledge of Relay, but chose it on recommendation from friends in my startup community and my web-dev colleagues. I was also warned about a somewhat steep learning curve, but decided to go ahead anyway - I am already fighting an uphill battle with JS and a 0.xx version of a new mobile platform, so what the hell, right? :-) I managed to set up my project correctly and punch a whole through to my GQL server with a QueryRenderer, which was a great relief :-)

So, on to the questions

I'm having a hard time figuring out the container/component relationship, and container composition in general. Reading the docs on composition helped, but I'm still in doubt over the role of the QueryRenderer

  • QueryRenderer is said by the docs to be the root container for every Relay tree. Does that mean that one should have a QueryRenderer for the root in our app? Or at the root of each navigation path (i.e. tabs in our app)? Or just for each container component (as opposed to presentational/dumb/pure components, React wise)? Note that I'm not looking for opinions, but arguments for best practice :-)
  • Can a FragmentContainer (or any other container, for that matter) work without a QueryRenderer in the ‘parent’ component?
  • How is the QueryRenderer linked to child containers? Does it fetch the sum of all the data that child containers want, and then the child containers read from the cache, or? If so, I’ve misunderstood the pros of Relay - we are under the impression that each component can retrieve data independently from every other components, and that each component does not know anything about the data requirements of other components (including parent/child components). I think this assumption is also what confuses me about the QueryRenderer, and the need for a “Root” container.
  • If QueryRenderer is a ‘parent’/‘root’ Relay container to a Relay tree, how come it has to render view components based on it’s request? And why does it have to have a request? When and for what should we use a QueryRenderer?

Any help is much appreciated :-)

Uprise answered 18/5, 2017 at 11:40 Comment(0)
U
13

So, after having worked some more with our app, I thought I'd come back to post about our thoughts and experiences so far, in the hopes that it helps someone.

Building on @Peter Suwara's great post, we arrived at a similar strategy initially

  • Have a root/parent nav tree
  • For each screen in the tree, have a QueryRenderer as the root for all presentational/dumb components it contains
  • Use fragments to declare data dependencies within the subcomponents

Like we discussed in the comments below his answer though, this approach might not always be the best. After further discussion internally, we came to the conclusion that there are too many cases where this might not make sense after all for a couple of reasons:

  • If you retrieve data for each screen-load, you often times will end up with more roundtrips than necessary. You want to take the app-flow into account, and fetch as much data as is viable/needed for a route and it's children.
  • Also, if you retrieve all data for the child components of a screen on the same QueryRenderer, you might sometimes end up fetching data that will never be shown to the user (i.e. details for a details view that is rarely shown, or similar cases).

After some more thought and discussion, we came up with this rule-of-thumb for when to create a new QueryRenderer 'root' of a Relay tree:

Create a new QueryRenderer for whenever you need a new error-handling strategy

This occured for the very practical reason that it's the only chance you have to handle errors/loading of data, but it also made sense to us in the way of user-flow and composition, as usually the two things collide - you want a new way of handling network errors when you reach a new 'flow'/part of your app. Maybe some smarter heads at Facebook thought about this before us? :-)

As with all rules of thumb, it's just that - a guideline, and not a rule, and it has exceptions, of course. However, it seems to make sense for us internally - at least for now.

Any and all feedback on these thoughts are very welcome, as I think the official docs are lackluster to say the least, and community discussion is therefore all we have until the docs and general patterns are settled upon with time.

Uprise answered 29/5, 2017 at 8:38 Comment(3)
If enough people upvote the answer, I will eventually mark it as "accepted" for whoever visits this post in the future. I'll await and see if others agree with our strategy first, though :-)Uprise
Isn't it Relay smart enough to fetch the data for fragments only when the component render?. For example, you might have a root query with a fragment ...User_friends; I believe that Relay will fetch data for such fragment only when the component that uses it renders. Please correct me if I'm wrongBootlace
Replying myself, I just tried a child fragmentContainer inside a parent QueryRenderer and indeed, the QueryRenderer fetches data for the child fragmentContainer, even when the child is not even rendered yet. Anyways, I'm going to have to upvote both answers, because actually it depends on the specific situation = )Bootlace
H
8

Thanks for bringing up this topic. I too have just been getting into Relay with ReactNative, and with some exciting results.

Firstly, I am surprised how easy it has been to bring UI components reflecting GraphQL databases to the screen. After the initial overhead of learning the basics of JavaScript and the react-native pipeline, Relay has become a fantastic way to present data.

In regards to best practices I cannot say for sure how to present your QueryRenderer and the fragmentContainer, however I can describe our way of presenting the data.

Firstly we create a react-navigation stack and tab. Inside each major screen we run a QueryRenderer. Then within that QueryRenderer, for each specific UI component, we seperate into a fragmentContainer.

  • Navigation Flow (react-navigation, Stack/Tab Navigators)
  • Screen (QueryRenderer) UI
  • Widget/Component (fragmentContainer)

This allows us to create the required primary query for the screen then break up the components data to fit within consumable components that are easily defined by the GraphQL query fragment they represent. However it does mean that we are running multiple queries across the app with no central account query to wrap the entire rendering up into a neat package.

Ideally I would like to try a QueryRenderer at the top inside a Navigator, however I haven't quite yet got my head around how and if this would work as Navigators do not respond to a render() function, which is where the QueryRenderer is required.

I would also be interested in hearing other peoples approaches to how they apply relay within a navigable react-native app.

Highpitched answered 23/5, 2017 at 0:2 Comment(4)
Hey Peter, thanks for taking the time to write such a detailed answer :-) After some discussion, that was exactly the same strategy we arrived at, and wanted to try it today when I saw your post - so maybe neither of us are that far off ;-) I, unfortunately, ran into another problem when trying to implement it, and posted it (jhalborg) here github.com/facebook/relay/issues/1665 . Once I get it working, I'll come back here and sum up my experiencesUprise
I do wonder, however, if this is the best approach if you have a deep nav tree in a stack nav as a child of one of the tabs - in such a case, the queryrenderer would fetch a lot of data that might not be relevant to the user before he hits a screen deeper in the stack nav tree, and so it might make more sense to include a second QueryRenderer as a new "root" further down the stack nav tree - what do you think?Uprise
Great to hear we are on the same page. Thanks for the the message. With regards to superfluous data. We are currently looking at this. For now, anything we do not need is just removed from the query. I have actually run into an interesting issue regarding meta data. Where UI does not reflect the results of a query, but is passed down using a prop. The details are quite complex so won't go into it here. But so far our solution seems to meet our requirements. It's definitely a lot faster to produce results than Native even if there are a few anomalies along the way.Highpitched
Also, check out the new SectionList -> facebook.github.io/react-native/releases/next/docs/…. We need to display different cell items in each section, each with a corresponding fragment from our query. Combined they make it so easy to show data in a neat UI. It's a little easier to deal with than a ListView with data source components.Highpitched

© 2022 - 2024 — McMap. All rights reserved.