-
Notifications
You must be signed in to change notification settings - Fork 786
Architecting for deeply nested UIs #192
Comments
We do something along the lines of
for the most important data, pass it down using Having said that, I'd be interested to hear from the react-apollo devs, since our architecture was originally based on relay and could probably be improved. EDIT: https://github.com/reactjs/react-router/blob/master/examples/passing-props-to-children/app.js#L55 might also be useful |
@stretchkennedy thanks for the feedback. I know how to pass props to children, but how can I do nested queries with variables? I don't think I've ever seen that in the docs/examples. I read the docs about batching again and I am wondering if all three queries in first try would be batched together? I don't really mind doing some slight over fetching or some extra queries. But if there's a relatively easy way not to do that, I would really like to know. Where/how to do it with |
With variables, the query would look like
The GitHunt example has some nested queries in it. The problem we ran into with batching was dependencies, i.e. component B relied on information from component A's query to render. If you don't have dependencies like that, batching does sound easier. |
@HriBB this is a great question! I'd love to give a thorough answer. Let me think through a few things and get back to you! |
@jbaxleyiii thanks! I've been thinking about this for some time now, going through all possible scenarios in my head, but there's a lot to take in. It would be good if more people got involved in this discussion, so that we can figure things out together. Maybe we can even create some good recipes for the |
@stretchkennedy when I write a nested query such as the one above, do I always need to query the entire hierarchy, or can I for example only query |
@HriBB you can query as much or as little as you like. Check out this example on swapi-graphql. To give a little more info on what we do, we'd have root fields of We'd write
or
depending on the UI. The reason I nested the queries when writing your example is that it looks like you need to know a booking's salon's slug to find the booking. If you can find a booking given only its id, then you can do what I did above. |
I won't pretend I'm an expert, but wanted to share my experience so far... I struggle with this questions every-time and usually can frame it as a trade-off in optimisation (e.g. avoid overfetching, loading from cache) vs decoupling (or modularity). Bellow I dig deeper in how I usually structure things. My architecture was inspired/copied from Relay - I even use graphql-relay - but with Using node interface is much better than creating a graphql end-point to each entity, you actually get this for (almost) free. But I don't see apollo community adopting, or being encouraged by apollo =(. Every entity I have can be fetched in the const query = gql`
query ($id: ID!) {
node(id: $id) {
id
... on MySpecificEntity {
field
...
}
}
}
` Now lets exam these 4 cases: 1 -Most decoupling: individual components with individual queriesNow, for example, if I want to fetch a list of stores to display : list of stores const query = gql`
query {
viewer {
stores {
edges {
node {
id #always fetch entities id
name
some_other_features
}
}
}
}
} I just fetch info necessary for the list of stores to display. Now if I click on the store, I have to open it with more info, like its products. So I have: individual store const query = gql`
query ($id: ID!) { # that store id previously fetched
node(id: $id) {
id # still need id here to cache properly
... on Store {
name
field
products {
...
}
...
}
}
} Look that the component that render the store only receives one prop: id. It assumes only this, almost nothing, and could virtually be called by any other component in page only passing the store id. This is as much decoupling as it can get. But w/o query diffing, you may overfetch (like field name). Also, you have to deal w/ delay of loading over network. 2 - Individual components, individual queries connected by fragmentsIf well, you actually don't want to fetching again every-time you click a store and actually want to fetch all info at once, I would recommend to create a fragment in the store component, and query the list with its fragment but also query the store with fragment. list of stores const query = gql`
query {
viewer {
stores {
edges {
node {
id
# same as before
...StoreFragment # fetch
}
}
}
}
} individual store export const fragment = createFragment(gql`
fragment StoreFragment on Store {
name
field
products {
...
}
}
`)
const query = gql`
query ($id: ID!) { # that store id previously fetched
node(id: $id) {
id # still need id here to cache properly
... on Store {
...StoreFragment
}
}
} Apollo will find the data in cache so won't refetch them (actually this is not how it works currently, but will soon be true). It is not totally decoupled because the list of stores now refers to the fragments in the store. But they are working together w/o much knowledge of each other, you are still only passing id. Note that will have delay for loading data from cache, but it is much less than over network. 3 and 4 - Passing props, most coupled: one single query, w/ or w/o fragmentsWell just fetch everything in parent and the pass props to children. You may have fragments in children to tell which data to fetch, improving modularization (notice that in situation 1 and 2, fragments increased coupling). But this is most coupled situation. It can introduce bugs: in Here there is no loading delay. Bottom line:
|
This would be a great thing for an article or two! I'm going to close it for now though since a few options have been discussed |
This is not an issue but a question about application architecture with
react-apollo
. Specifically with deeply nested UIs.Let's say I have the following
react-router
routes definedWhich translates into the following URLs
I am trying to see the big picture on how to structure the client, but this is a bit beyond me ATM. So I am hoping for some help from the community. But it's really hard to ask these kinds of questions, because of the complexity. Anyway, I will try ...
Let's say user visits the following URL:
This is my thinking flow ...
First try:
salon(slug:"some-slug")
insideSalon
component then render children once I have the datasalonCustomer(salon:"some-slug", customerId:123)
insideSalonCustomer
then render childrensalonCustomerBooking(salon:"some-slug", customerId:123, bookingId:456)
insideSalonCustomerBooking
and renderBut that is far from optimal. I am overfetching data and doing multiple roundtrips to the server. Not good.
Second try:
salonCustomerBooking(salon:"some-slug", customerId:123, bookingId:456)
and render.No overfetching, a single roundtrip to the server. Much better. But ... what if some component up the hierarchy wants to render some other data? For example, what if I want to render some basic
Customer
details inSalonCustomer
? Hmm ... not good.Third try:
Salon
component then ... send all the data down the hierarchy through props?Basically, this is where it stops for me and I need your help. How, when, where, ... Of course I could go and start experimenting, but it would take me days if not weeks to figure out the best way to do it.
How would you do it?
You can close this topic, because this is NOT an issue with
react-apollo
. It's just me not seeing the big picture :) And I am sorry for "spamming" the issue tracker with questions.But I think that it would be good for the community (at least for those of us that don't have much experience with GraphQL clients/servers yet), if we had an example for a more complex app, once the API is finalized. Maybe something like the huge-apps example for
react-router
. I can do a POC once I understand things.Oh and BTW: great job on the new docs ;)
The text was updated successfully, but these errors were encountered: