Apollo's local state tools are bad in my opinion as well. It might work OK if you just want to keep a few global flags, but anything more, and it gets really verbose. Especially if you want to update state based on current state, you have to first manually fetch the current state, then update it, then save that object back, every time. On top of this, its often necessary to write mutations just to update local state, which are written as GraphQL strings - one mistake in this syntax and you can be stuck trying to debug it for a while. How any of that is easier than just having Redux I still don't understand.
Overall, Apollo feels like a worse version of Angular.js - an app framework thats trying to do way too much. If your app just made basic GET requests and had no other logic, I could see how Apollo could be useful (but why have a framework at all in this case?) However, if you have complex logic or local state, Apollo quickly becomes and opinionated, unwieldy piece of junk
> in reality, its a bad idea to have your view layer directly making API calls - those should be in some other module thats lower level, away from the view and easier to test.
Apollo doesn't force you to cram everything into your view layer. If you want full control over how your interact with your GraphQL backend, you can wrap "Apollo Client" in whatever layer you want. If you're using a view layer integration like "React Apollo", we try to help reduce some of the extra layer boilerplate that can come up, when integrating your view layer with other controller, model, business, etc. layers. Again though, none of this is mandatory, and it's important to understand that components in "React Apollo" aren't directly making API calls. Query, Mutation and Subscription components are just React components, that communicate with Apollo Client behind the scenes. Here, "Apollo Client" is the lower level module that you're referring to, that then takes care of API calls. As for testing, you can (and should if possible) test all parts of your application, be it view layer or lower level API integration points. We provide tools to help unit test React Apollo components with mock/fake data (that can be integrated with any popular React testing library), and there are lots of great ways to unit/integration test Apollo Client (and its associated libraries).
> But instead, people use these query components everywhere, and all of the sudden, the view balloons with logic like parameter validation, error handling, etc... Everything is weaved into the view and very hard to unit test.
React gives you a toolset to work with and manage components. Can you build an application that gets out of control using React? Absolutely. Apollo gives you a toolset that can be used to work with data. Can you build an application that gets out of control with Apollo? Most definitely. Just like the React team, we've taken a lot of steps to help developers avoid these application design pitfalls. We spend a lot of time working on our docs, routinely blog about best practices, and help developers whenever/wherever we can, through various social channels, meetups, conferences, etc. That being said, we've worked side by side with hundreds of developers who are using Apollo happily and productively, and if anything, have found that Apollo's view layer integration has helped them get their countless view layer data integration points under control (and made things easier to test).
> Apollo's local state tools are bad in my opinion as well. To update any state, you have to constantly fetch the current state and then make a modification.
This isn't accurate. Actually, in most cases saving and updating local state is as easy as just running a query/mutation - everything happens for you seamlessly. Apollo Client's local state capabilities have changed quite a bit as of version 2.5, so give a newer version a try (and see the updated docs) to see if that helps.
> This feels a lot more cumbersome than just having redux and reducers.
Redux can definitely come in handy, but it has its strengths and weaknesses (like all tech). One of the core tenants of Apollo Client's local state handling is that it knows how to work with GraphQL out of the box. This means AC's local state handling can be used to do all sorts of great things, that would require more work to accomplish with something like Redux. Things like working with a specialized cache that normalizes GraphQL query responses to speed up cache reading/writing, know how to merge local state with results returned from remote queries so applications get everything they need from local and remote after running a single query, replicate remote GraphQL resolver functionality locally to inject advanced data management techniques on the client side, etc.
> Overall, Apollo feels like a worse version of Angular.js - an app framework thats trying to do way too much. If your app just made basic GET requests and had no other logic, I could see how Apollo could be useful. But anything more complex, and the code becomes a weaving, untestable mess.
Some of the biggest companies on the planet (with amazingly complex applications and systems) are using Apollo and loving it. That being said, we're always striving to do better and welcome constructive feedback like yours. Please consider opening issues in any of our Github repos with your problems and suggestions regarding how we can help make things better.
You are glossing over the fact that there is a 3rd part, writing a reducer! This part is the most cumbersome in my opinion.
Lets say you wanted to keep a counter and increment it in your app. With Redux, you add it to your state, write an "increaseCounter" action, and then write a reducer. This reducer is just a plain function without any dependencies. A unit test for this reducer would just test that the count was updated.
However, with local state management with Apollo, you would either need to
1. write an incrementCounter mutation and an incrementCounter reducer. Inside that reducer, you would have to fetch the current count from the cache, update that count, and then write the results from the cache.
2. Just use `cache.writeQuery` or `cache.writeData`, but in either case, you need to fetch the current state of the cache, modify it, and then write the object back.
The problem with this is that any time you want to modify state, you need to always fetch the current state, and then write the state back. This is bad because
1. its super repetitive
2. it tightly binds the code of "how the state changes" to "how the state is queried and saved"
Overall, this whole approach of having to write a mutation and a reducer for every state change is annoying and verbose. Redux is just overall a better, more convenient pattern to incrementally update a piece of state.
We actually thought about adding a Redux layer on top of Apollo, so that we would just have a single "Redux" mutation that fetches the current state, passes it to a reducer, and then saves the transformed state in the cache to avoid all this fetching and saving, but then were afraid of potential performance issues of homerolling our own Redux, and went with actual Redux instead.