Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can I access the Apollo Client Redux state reliably? #1321

Closed
ozyman42 opened this issue Feb 23, 2017 · 26 comments
Closed

Can I access the Apollo Client Redux state reliably? #1321

ozyman42 opened this issue Feb 23, 2017 · 26 comments

Comments

@ozyman42
Copy link

ozyman42 commented Feb 23, 2017

Is this structure meant to be internal to apollo client, or is its current structure a supported part of the API which will remain constant in upcoming versions?

If it will change, then I'm probably going to either take my GraphQL query results and store them in a custom portion of the Redux state, or if there's a standard way to access the results of a query between components then I'd be interested in knowing that standard way.

@calebmer
Copy link
Contributor

The Apollo Client Redux state is an internal implementation detail. We do not recommend that you depend on the format because it may change at any time. Even depending on actions can be dangerous because we may change them at any time too.

We are, however, working on building tools to help you get certain kinds of data from the store that is relevant for building your application. Such as providing readFragment and writeFragment methods in this PR: #1310

Let us know what information you need from Apollo Client that we aren’t currently providing. There may be a good feature in here that we could add 😊

@stubailo
Copy link
Contributor

access the results of a query between components

Would client.query work for your needs?

@ozyman42
Copy link
Author

Thanks @calebmer

@stubailo I'm not sure what client.query is. I don't recall reading about it in the documentation.

@stubailo
Copy link
Contributor

@alexleung if you get a reference to the ApolloClient instance (perhaps by importing it) you can run any queries you want outside of a component. What are you trying to do?

@ozyman42
Copy link
Author

What I'm currently doing is having one of my application's root components run a query which fetches all the starting data for the entire app, then I'm storing it in redux when props.data.loading changes from true to false. Once it's stored in redux, then a number of other components in the application are using that redux data to populate themselves. I was hoping that there was a way I could cut out the step of taking the query results and storing them in redux.

@stubailo
Copy link
Contributor

@alexleung why not populate the rest of your components using GraphQL queries? That's exactly how Apollo is designed to be used - you can use fragments to allow your components to specify their data requirements, do one query, and get all of that stuff at once without having to write any custom redux code at all.

@ozyman42
Copy link
Author

ozyman42 commented Feb 24, 2017

I could, but each of those queries would probably need some parameters to be sent with them that come as props into the component, and as far as I'm aware, one can't use a component's props in the variables portion of options when using the graphql function.

@ozyman42
Copy link
Author

ozyman42 commented Feb 24, 2017

As in, I can't do this:

@graphql(someQuery, {
    options: ({props}) => ({
        variables: { objectId: props.id }
    })
})

@calebmer
Copy link
Contributor

calebmer commented Feb 24, 2017

Yes you can. The first argument of your options function is props 😊. options wouldn’t be very useful otherwise. The following should work:

@graphql(someQuery, {
    options: props => ({
        variables: { objectId: props.id },
    }),
})

@calebmer
Copy link
Contributor

I’m going to close because the original question was answered, but please feel free to ask us more questions if you need 👍

@ozyman42
Copy link
Author

ozyman42 commented Mar 11, 2017

Another reason why I wouldn't want all of the components to be populated via GraphQL queries is because I don't want to be making new requests all the time when a user changes the page (via react-router), going from one view component to the other.

I'd prefer to use the model of loading as much of the data as possible when the application initializes, then having a set of GraphQL subscriptions update the data as it changes (which I have been able to set up).

Would it be beyond the scope of apollo-client to provide a standard way of accessing the normalized result of graphql queries for such a case?

@calebmer
Copy link
Contributor

@alexleung check out the new read and write methods we added to Apollo Client, docs are here: https://github.com/apollographql/core-docs/blob/master/source/read-and-write.md (they should be deployed to the website soon). Let me know if those fulfill your needs 😊

@ozyman42
Copy link
Author

Ok this actually does seem to be what I was looking for. Thanks @calebmer!

@0x6e6562
Copy link

0x6e6562 commented Mar 14, 2017

@calebmer The new read and write methods look like a pretty cool evolution in the API. Reading the section titled "Updating the cache after a mutation" - will the direct proxy access ultimately supersede the current updateQueries mechanism? I'm wondering whether code that uses the current declarative cache update mechanism should be ported to the proxy API in the medium term, or will both mechanisms co-exist in the future?

@calebmer
Copy link
Contributor

@0x6e6562 we are looking to see if update is a viable replacement to updateQueries, yes, but that requires a lot of feedback from the community! 😊

If you could try using this new approach instead of updateQueries please let us know your experience. We know people are having issues with updateQueries and we want to find a better paradigm, but if people like updateQueries and we don’t have a convenient alternative then we are not going to remove it. Let us know what you think 👍

@0x6e6562
Copy link

Thanks for the heads up. I'll give this a go - I assume that I need to update to 1.0.0-rc2 or similar?

What lead me to this thread was needing (or at least thinking that I need) to update the local cache as a result of having received a push notification. In this use case, I'm not using a subscription (because the back end I am using doesn't support this). So I'd like to use the notification to either trigger a refresh or preferably directly update the local cache. I'm thinking that updating the cache is neater because it fits with the overall view update paradigm.

I can see the maintainability issues that arise with the current updateQueries mechanism. That said, it is still quite a declarative API, and once you've understood it and the effects that it has on cache state, then it's reasonable to work with.

In an ideal world I would like an API that allows application code to subscribe to the graph in the same way that view components subscribe to the graph. I think this what the reduceroption attempts to generalize on a mutation. But I the thing I don't get is why this callback code has to be co-located with the definition of the mutation (e.g. when you wire up a mutation with connect).

Why can't you have a centralized mechanism to subscribe to specific nodes on the graph? Potentially I've completely missed the point, but if view components are subscribing to the server side graph, why can't a client side code block hook into the client side graph?

Or should one be using regular redux reducers to tweak the client side graph?

@calebmer
Copy link
Contributor

Why can't you have a centralized mechanism to subscribe to specific nodes on the graph?

This is certainly another primitive that we want to add. It is harder to do compared to the read/write methods, but you can currently get this behavior within 90% accuracy with watchQuery and a cache-only fetchPolicy:

client.watchQuery({
  query: gql`{ ... }`,
  fetchPolicy: 'cache-only',
}).subscribe({
  next: () => { ... },
});

I believe this should work for what you are describing. Let me know if I’m wrong!

@0x6e6562
Copy link

Can writeFragment be used an incremental way to update a single leaf within a node or does the entire updated state of the data field need to passed in?

@0x6e6562
Copy link

Regarding the cache-only option - this is really cool and I didn't know that this existed - I guess this is like having a headless redux subscription. In certain situations this could be quite useful.

After having asked my question I've realized that there's something about my own question that doesn't make sense: I was trying to avoid having fragments of mutation side effects distributed over the application code base and I was asking how to centralize the graph subscription. But I am actually doing with updateQueries is optimistically injecting new mutation state into the graph ahead of the next server query. So that mutation has to come from somewhere, and it can logically only really be known to the component that triggered the mutation. So this is why the updateQuery callback needs to be co-located with the component.

But as a side effect of my bogus question, I've learnt about cache-only, so that is cool :-)

@0x6e6562
Copy link

Re-reading the documentation, I think I might have partially answered the writeFragment question. It seems that since you have direct access to the cache, you can read the fragment just before updating it. So this give you more explicit access to the cache state than the updateQueries mechanism.

However, upon trying this out, I ran into an error that requested the user to file an issue: #1415.

@0x6e6562
Copy link

As indicated in this comment it is important not to forget the __typename field when updating sub nodes using writeFragment.

@calebmer
Copy link
Contributor

Can writeFragment be used an incremental way to update a single leaf within a node or does the entire updated state of the data field need to passed in?

Yep! You just need to be sure to only pass in a fragment with the field you want to update 😊

fragment oneField on User {
  name
}

vs.

fragment manyFields on User {
  name
  age
  hairColor
  ...
}

Glad to hear you figured it out 🎉

@stubailo
Copy link
Contributor

@0x6e6562 if you have some ideas for how we could document these patterns, PRs are always appreciated!

@0x6e6562
Copy link

@stubailo That's a very reasonable suggestion - right now however I am fumbling my way around the new API and don't quite feel I've groked every aspect.

I've just figured out how to insert new items into an empty subscription, but now I'm at the point where I'd like to delete stuff from the cache.

@calebmer Is there an API to delete a fragment?

@0x6e6562
Copy link

@calebmer Or is deleting an element from a collection isomorphic to adding a new item, i.e. read the current collection state and then splice on the key that you want to nuke?

@stubailo
Copy link
Contributor

@0x6e6562 that's right!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 17, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants