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

[2.0] ApolloClient.reducer() method is gone, how to connect to redux? #2273

Closed
lrettig opened this issue Oct 8, 2017 · 44 comments
Closed

[2.0] ApolloClient.reducer() method is gone, how to connect to redux? #2273

lrettig opened this issue Oct 8, 2017 · 44 comments

Comments

@lrettig
Copy link

lrettig commented Oct 8, 2017

Intended outcome:
Create a new Apollo client, then plug it into redux as a reducer per these instructions.

Actual outcome:
Error: apolloClient.reducer is not a function.

How to reproduce the issue:
I tried to do this using both code-sandbox and react-apollo-error-template but neither has been updated for 2.0 and neither would work with the beta code. Here's what I'm trying to do:

import ApolloClient from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { combineReducers, createStore } from 'redux';

const httpLink = new HttpLink({ uri: 'https://some.data.url/' });
const apolloClient = new ApolloClient({ link: httpLink });

const store = createStore(
    combineReducers(
      {
        apollo: apolloClient.reducer(),
        someOtherReducer,
        ...
      }
    ));

Version

@lrettig
Copy link
Author

lrettig commented Oct 8, 2017

I'm also getting the error apolloClient.middleware is not a function. It looks like the culprit for both may be this commit: 2b52e53

@n1ru4l
Copy link
Contributor

n1ru4l commented Oct 8, 2017

@lrettig apollo-client v2.0 replaced the redux cache with an abstract cache implementation. At the moment there are two implementations that i am aware of: https://github.com/apollographql/apollo-client/tree/master/packages/apollo-cache-inmemory and https://github.com/convoyinc/apollo-cache-hermes (none of them use redux).

Therefore you would need a redux cache implementation that implements an interface for exposing its reducer.

You can read about all major changes here: https://dev-blog.apollodata.com/whats-coming-in-apollo-client-2-0-bcd8ea64acbd

@jbaxleyiii
Copy link
Contributor

@lrettig that is correct, apollo-client no longer has a link to redux by default. What do you need the link for?

@lrettig
Copy link
Author

lrettig commented Oct 10, 2017 via email

@jbaxleyiii
Copy link
Contributor

@lrettig you don't need to anymore! Any reason you want to?

@SkReD
Copy link

SkReD commented Oct 11, 2017

@jbaxleyiii
From the upgrade guide to the 2.0 version:

The next version will also take steps to reduce the overall size of the default client and provide the foundations for Apollo powering more of the application experience from development to production (i.e. client side state management).

Doesn't this mean that apollo become the state container for all the data in application and not only for graphql data? If so, than this is what redux do and it is good at it, with all the plugins and stuff, like time traveling, around it and the concept itself.

So why not just go in other direction and provide the ability to run requests through the redux actions and get the apollo data through selectors from redux store like such libraries as redux-form do. Why different store which should be controled when persistance, for example, applied or when history navigation should be controlled and you need to move application to the state, before previous navigation. In the way you suggest it need to control two stores instead of one. And you will need to provide instruments, like redux already have, to manage this state and to debug it.

@n1ru4l
Copy link
Contributor

n1ru4l commented Oct 11, 2017

@SkReD AFAIK the removal redux store move is related to performance and pluggability. Not everybody is using redux. There are users out there that use mobx or vue or ember or . It is now possible to replace the cache with an implementation that fits your needs. Related: #1432

@playerx
Copy link

playerx commented Oct 19, 2017

cache implementation abstraction is good, but not having redux implementation out of the box is bad, because right now I'm using redux for time travel, epics and I don't want to just drop it and replace with graphql client, because building chains with epics is really cool stuff. So I will not be able to move on 2.0 until there will be redux package for Apollo client 2.0. so will be other developers who use redux.

P.S. Great features in 2.0 but redux support (via middleware) is must have I think

@OllieJennings
Copy link

OllieJennings commented Oct 26, 2017

IMO there should be a redux cache implementation to make it easy to upgrade from 1.0 to 2.0. Right now am on 1.0, and won't be able to migrate to 2.0

@lgants
Copy link

lgants commented Oct 27, 2017

@jbaxleyiii I wanted to use redux since I need to pass local data (that changes on user selection in a dropdown menu) around to multiple components. In my opinion, this was an important point of differentiation for Apollo from Relay. Is there a workaround for Apollo 2 that I'm not seeing?

@andr11111
Copy link

I agree, getting rid of redux is a premature decision IMO. Somehow I still need to manage the local state and redux is a proven solution for this.

@sharonswli
Copy link

sharonswli commented Nov 9, 2017

Any updates on when/if a apollo-cache-redux integration will be available as a package in the near future? Is this something on Apollo's roadmap or is currently being worked on? If not, where can we find some documentation on how to integrate our own custom store implementations to the new generic Apollo Client Store API ?

I understand the decision to move away from the Redux dependency to open up more options on customizing our cache with different store implementations (ngrx, mobx, etc..), but like many others, one of the main reasons why we opted to use Apollo in the first place is its ability to integrate GraphQL with existing Redux stores to manage local state, as well as to take advantage of the many handy tools and integrations that are already available within the redux ecosystem (dev tools, epics, etc..). Removing that option entirely without an appropriate replacement poses a problem for those of us who DO wish to use redux as our cache implementation, but are now stuck in v1.0, or stuck with using the default Apollo Cache Store which doesn't come with nearly as much community support than redux.

@christopherdbull
Copy link

So what are people doing to use Redux? I was about to adopt apollo into a redux app, but I can't figure out how to use V2 with the app without a whole lot of unnecessary duplication which I don't want to do, as I still want my app's main flow to go through redux.

@natterstefan
Copy link

I am struggling to since hours to get Redux working in Apollo 2.0 but no success so far... I do not understand - like many others here - why they have dropped it completely... Does anybody got it working (again) in the latest (react-)apollo version?

@playerx
Copy link

playerx commented Dec 4, 2017

I had same feeling first, but now I've realized several facts:

  • You have to use apollo client to access local store, every time
  • Redux is an alternative of GraphQL, at first I thought that Redux was a lower level and graphql was higher level in my application architecture.

Now I think that this separation is great that apollo team made, there is only one use case when I may need redux store:

  • When I need to have some additional local data in store and to read & write them. Apollo graphql client offers readQuery, writeQuery so you can achieve that also.

for me trade off is that I don't have time travel debugging out of the box when I use apollo graphql client, but there are so many features actually I don't feel any discomfort about that.

@natterstefan
Copy link

@playerx Hm. Thanks for the answer, will think about that. Still struggling with some things. But I have a question:

You have to use apollo client to access local store, every time

Can you show/tell me how in more detail please? (Note: relatively new to the entire Apollo ecosystem, that's why I may struggle quite a lot currently).

When I need to have some additional local data in store and to read & write them. Apollo graphql client offers readQuery, writeQuery so you can achieve that also.

How can you achieve this with apollo-client? Eg. in Redux I had some states which were relevant for multiple components. They needed to know the latest state, but not gql data, more like current app-state-data or such.

How can I make this available to my app with apollo-client now (because they dropped redux)? That's what I am not getting into my head right now ^^

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 4, 2017

@natterstefan You could either locate the state somewhere higher in your component hierarchy and pass it down. You could use redux alongside apollo-client or you could try https://github.com/apollographql/apollo-link-state (not production ready yet i think)

@playerx
Copy link

playerx commented Dec 4, 2017

@natterstefan there are two different case scenarios:

  1. read/write data to client side store
  2. extend server-side schema in client side

  1. to read or write data to the store we can use: https://www.apollographql.com/docs/react/basics/caching.html#writequery-and-writefragment

  2. to extend server-side schema and add new fields or types with their own resolvers there is apollo-link-state: https://github.com/apollographql/apollo-link-state

I think you can achieve your goal using those two different features. for subscribing to specific fields and getting latest updates you can use watchQuery with valueChanges (this is for Angular, but same is for other frameworks, idea is the same) https://www.apollographql.com/docs/angular/basics/queries.html#options

@natterstefan
Copy link

@n1ru4l yeah, tried to use both Redux and Apollo-Client, but had issues to put the apollo state into the Redux store (both with the latest available version)

@playerx Thanks for your answer. So:

to read or write data to the store we can use: https://www.apollographql.com/docs/react/basics/caching.html#writequery-and-writefragment

means I need to write such a query/fragment and store the things (I usually put into the Redux Store) into the Apollo one?

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 4, 2017

@natterstefan Could you please explain why you want the apollo state inside your redux state?

Also there is this WIP https://github.com/rportugal/apollo-cache-redux

@playerx
Copy link

playerx commented Dec 5, 2017

@natterstefan Yes I think that's the correct way to write data in store, when you are using GraphQL Client. You shouldn't have direct access to redux store, because graphql client makes data normalization and you should not know how it transforms and stores data, you need some higher level api like writeQuery, writeFragment. Based on that I think you should not use redux store any more and in-memory cache is better choice.

@natterstefan
Copy link

@n1ru4l @playerx First of all, thanks for yur time guys. Let me try to explain my scenario in more detail. Because probably there is just one missing link in my head, or I understand something terribly wrong ^^

Image the following basic setup:

  • Let's say I have a web-app with some "UI" components (they are just for interaction, but they should also react according to the data I query. eg. a Sidebar Menu) and there are some "Data" components (they query data from the server/db with gql, react-apollo etc. and do something with it. eg. a List).

Now I want to achieve something like:

  • Let's pretend a List-Component queries data from the server (with apollo) and displays it. For some reason, I want the Menu to show another NavItem (let's ignore the question why I want to do it, just for the sake of this example), when the list contains a certain value. Previously I would have done this probably with Redux and then update the property with mapStateToProps. Now I am confused. ^^

  • Or let's say there are other properties of the query result I want to share with another component on this page, so that the other component can do something with it. Does this other component has to make a request themselve or how can I share the data with others, like I previously did within the redux store and mapStateToProps, etc.?

Thank you for your help.

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 5, 2017

@natterstefan You can pipe the connect higher order component with the graphql hoc.

I would solve it by using lodashs flow

const MyComponentWithStuff = flow(
  connect(
    mapStateToProps,
    mapDispatchToProps
  ),
  graphql(QUERY, {
    // here we are also passing trough the redux selection set
    props: ({ ownProps, data }) => ({ data, ...ownProps }) 
  })
)(MyComponent)

The second problem depends on the use-case.

If both components are rendered at the same time and you know that always both components will be mounted at the same time you could use the fetchPolicy option to only load the second query from the cache.

Also you could use apollo-link-batch-http (this does not reduce the amount of queries but batches them so there are less requests).

Additional cacheResolvers could possibly also help you out, e.g. if you want to display a single item that is part of a large list without making that roundtrip to the server.

@natterstefan
Copy link

natterstefan commented Dec 5, 2017

@n1ru4l Thank you very much for your answer. Haven't tested it (yet), but it looks really promising. Additionally, it helped me to get around with some issues I had in my mind.

If both components are rendered at the same time and you know that always both components will be mounted at the same time you could use the fetchPolicy option to only load the second query from the cache.

What if they are not rendered at the same time, can they still query/get the data from the internal apollo store/cache (somehow)?


Let me quickly try to summarize how I understand it now, and correct me if I am wrong:

Am I missing something or am I totally wrong?

//cc @Lumannnn

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 5, 2017

@natterstefan If the data is already in the store the component that is mounted later will use the data that is already in the cache (if all the fields your query specifies are present). This is the default fetchPolicy cache-first.

There are some more policies in the docs which I linked in my previous comment.

Yes components do update automatically. When you delete objects or add objects to a list however you will have to use the imperative store api (readFragment/writeFragment/readQuery/writeQuery) to update the query. Since apollo does not know when an object was deleted.

@natterstefan
Copy link

@n1ru4l Thank you!. I think I definitely have a better picture of the entire apollo "ecosystem" now.

For the record, this is a great article from Peggy Rayzis about the topic. Just read it a few minutes ago.

Now it's the time to make an example work to see it in action. Because that's what I am missing, an example that puts this into practice. All examples I found so far only illustrate use-cases with 1 query, no accross-component interactions and redux-like behaviour with apollo 2. Or do you know one?

@ghost
Copy link

ghost commented Dec 5, 2017

@natterstefan
Copy link

@n1ru4l FYI, it looks promising right now that I can use Redux as my UI-Store (for UI states) and Apollo as my Data-Store (for db, local/client data). The only thing I did not yet figured out properly is how I should share the client (apollo) with my HoCs (like one of these: #2273 (comment)), to let my component readQuery, WriteQuery etc.

How do you do that usually? I am just looking for a best-practice thing or how you guys from apollo suggest it :)

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 8, 2017

@samueldepooter
Copy link

What about caching Apollo store data in localstorage? For Redux I used redux-persist. Is this something I will have to do manually in Apollo?

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 8, 2017

@samueldepooter You should have a look at https://github.com/jamesreggio/apollo-cache-persist

It is currently a pre-release, but expect more in the near future 😉 For the meantime you could test the pre-release, give feedback or even contribute.

@samueldepooter
Copy link

samueldepooter commented Dec 8, 2017

@n1ru4l Thank you for the response! Sadly I'm currently using create-react-app to test small things and I'd like to refrain from using eject to manually solve the error it's creating (more info in this issue)

One more question: how exactly do I watch Apollo store changes? So if component 1 updates something in the cache by a writeQuery, how can I listen to this change in component 2? Or is this simply not possible with only Apollo?

@ghost
Copy link

ghost commented Dec 9, 2017

Gentlemen, after read all this can I ask you what do you think about this? https://stackoverflow.com/questions/47655399/react-apollo-2-graphql-authentication-how-to-re-render-component-after-login

It's a simple problem I think, but today is still unanswered. I come from Redux, and I'm trying to not use it anymore with Apollo 2. How do you accomplish this?

Here we are talking a lot: developer239/react-apollo-graphql#14 (comment)

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 11, 2017

@samueldepooter Could you explain your use-case? Actually the second component should receive the new cache updates and if you want to do some side effects use your components lifecycle hooks (componentWillReceiveProps).

@samueldepooter
Copy link

samueldepooter commented Dec 11, 2017

I have a component that executes a query to fetch a list of data. For each of these items in the list I have a button that updates the item using writeFragment. The other component (not linked to the list) should listen to any change in one of the list items and display information about the item that I just updated. But at the moment I can't seem to intercept the change in my second component. What I'm missing is how the second one is aware of this cache change, meaning how do I make the second component "listen / watch" this part of the cache?

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 11, 2017

@samueldepooter I think it would be best to open a new issue for this. Actually the call to writeFragment for an object that has a unique id (cache key) should trigger an update of queries that have the object in their selection set.

@samueldepooter
Copy link

samueldepooter commented Dec 11, 2017

@n1ru4l Another problem is fetching the data in the second component. I know you can do readyQuery but it throws an error if the data does not exist yet. So if component 1 fetches or writes the data and component 2 needs to access this data, it's not possible to use readQuery in component 2? Or where would I have to use it in the component 2's lifecycle? I tried several things and it didn't work so maybe this is where my problem lies.

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 11, 2017

@samueldepooter Why would you even use readQuery over the graphql HoC?

@samueldepooter
Copy link

samueldepooter commented Dec 11, 2017

@n1ru4l When it needs to be executed on a user's action? For example on hover/click/... And also when you use query in graphql HoC, it will directly try to do the server call (which I don't want, just need to fetch existing data from the cache)

@n1ru4l
Copy link
Contributor

n1ru4l commented Dec 11, 2017

@samueldepooter Could you please open a stackoverflow issue or ping me on the apollo slack? I do not see how this is related to not having redux and furthermore this seems like very odd or advanced use case.
Also please try to explain exactly why you want to do this and why you prefer to use writeQuery over the things I mentioned earlier in this issue.

@samueldepooter
Copy link

Will do, thanks for your help already!

@0xjmp
Copy link

0xjmp commented Oct 31, 2019

Redux use case: legacy code base

Team was excited to be finally getting off redux and doing apollo. Thousands upon thousands of lines of redux code built up over the years. We foolishly thought we could adopt Apollo in a new feature and slowly refactor out the redux code while having them hooked up together. So you could say this is a huge letdown.

So you have a perfectly valid use case for redux. I'm curious... what is your use case for removing redux integration? If it was a maintenance thing then by all means show us where your dead code is and we'll pick it up and get everyone in this thread excited about adopting Apollo again.

Update: thankfully, Relay has a clear path for us to start using it alongside redux. Bon voyage 🚀

@perepelitca
Copy link

Hey, @JakeDawkins!
We are trying to add gql/apollo integration to existing Redux project and I think we faced the same issues as you did. So far we haven't found a clear way how to use Apollo alongside with Redux. Could you share your experience how you did it for Relay?

@felire
Copy link

felire commented Aug 20, 2020

Hi! Is there any way of doing this? I would like to add a false reducer and mount a false store on dev mode to be able to put all the cache on the state and use the reactotron state for debugging

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 15, 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