Introducing Loona: Application State Management
Loona is an application state management library for React and Angular. It helps you manage the local and remote data in a single store. It’s built with GraphQL and Apollo.
Me and The Guild are very excited to introduce a library that dramatically improves and simplifies managing your local and remote data, both in React and Angular.
Hi, my name is Loona!
What is Loona
Loona is a state management library built with GraphQL and Apollo. It takes some the of Redux / NGRX concepts and combines them with Apollo Client.
Loona works side by side with Apollo
So while you still get all the benefits from Apollo Client like caching, offline persistence, normalized cache and more, on top of that you gain other benefits like stream of actions and better separation between executing a mutation and updating the store.
Instead of having a second store for your local data, keep everything in just one space.
We built it on top of Apollo Client which means setting everything up, including Links or even Cache, stays exactly the same.
Loona can be described by few core concepts:
- Queries — ask for what you need
- Mutations — a way to modify your remote and local data
- Store — a single source of truth of all your data
It also defines:
- Actions — declarative way to call a mutation or trigger an action
- Updates — modify the store after a mutation
It helps you to keep every piece of your data’s flow separated.
How to get started
First, you need to install Loona depending on a used framework.
yarn add @loona/react
yarn add @loona/angular
Then, you create Loona very similar to what you would do with Apollo.
I’m going to use React examples in the article, but same logic applies to Angular. The API is pretty much the same.
How does it compare with Apollo Link State?
But by extracting some logic from the resolvers, it introduces a new way of updating the store that allows to persist mutations with their side effects across page reloads or while app is offline.
In fact, the logic behind Effects (middleware) opened up Loona for all kinds of tooling and capabilities.
It also provide some helpers that are based on
Immer so the whole experience is much better.
By keeping Loona close with Apollo Link State we are able to collaborate and improve both of them.
Play with Loona
You can find few examples in our repository but we prepared a simple application that you can run and play with in the browser.
One written with React:
Loona, step by step
Now since everything is ready, let’s take a closer look on what Loona does and why.
State is a model that describes slice of your application’s state, with all possible mutations, queries and others. It’s important to keep it simple and easy to read.
As you can see, we use
state decorator and a class to define a state.
Queries work just like in any other GraphQL client, you simply ask for data and receive it either from a cache or a network. Both, remote and local data can be used together in a query.
We used a
Query component but Loona also allows to use the HoC.
BooksList component asks for data, it gets an empty array.
We also support Higher Order Components. Learn more about queries.
Think of an Action as a declarative way to call a mutation or to trigger a different action based on some behavior.
Let’s take a look at the example. Here’s our action called
Loona has an
In the example above, we dispatched an action and as the
type says, it should somehow add a new book to the list.
To listen for an action we have a concept called effects (works for both, actions and mutations).
It’s also possible to dispatch a mutation. More about Actions on the website.
Update allows to modify the data based on a mutation.
The whole idea behind Updates is to keep store updates separated from mutations. Updates work for remote and local mutations.
What are the benefits of using Updates?
- you no longer keep the code responsible for store updates inside of your components
- it scales easier
- keeps the code clean
- allows to execute them even after mutation that has been persisted, did run again
This is probably the most important piece of Loona’s data flow.
In the example above, we separated two things:
- creation of a book (happens in the mutation)
- adding a book to the list (happens in the update)
Why would we need many Updates?
Imagine a state that not only has a list of books but also holds a recently added one. Instead of having two updates in the mutation resolver we can scale that and pass the book to any function.
We covered Updates in the documentation.
About the API
Loona doesn’t require decorators, it provides an API to consume the state without them.
If it comes to consuming data in the UI, we decided to enhance and reuse
Subscription components of
react-apollo and also provide two helpful Higher Order Components:
To learn how to use those components, we prepared a chapter in our documentation.
How does it compare with other libraries?
Make it work offline
With Loona and Apollo you’re able to persist the cache and restore it on every page load.
Thanks to keeping the logic responsible of store updates, outside of a component, we’re able to persist mutations across page reloads or while an app is offline and rerun them once it’s up again. The whole chain of actions and updates will then continue.
The store is smart enough to re-render only components affected by an update. Thanks to keeping data normalized you can mutate an entity and the store will update every reference.
It would be hard to keep a list of books and their authors in a structure as shown above in Redux-like libraries and to still be able to update everything easily by touching only a single book.
Because the data is normalized stored flat it is super straightforward to do it in Loona. Having helpers like
patchFragment that uses
immer internally helps a lot too.
Single source of truth
When using GraphQL with Redux or any other state management library it’s hard to keep two stores in sync. With Loona you keep everything in one place and local data can enhance the remote one.
Unified interface of all data
You access data in the same way across the whole application and thanks to Apollo Link, for example
apollo-link-rest, you can include other source of data too.
You can even enhance the server-side operations with your local state and fetch everything in a single query.
No need to implement things like Caching or Optimistic UI yourself, it’s part of Apollo and Loona.
It’s also possible to provide a lazy loaded state when using code-splitting
Easy static type generation
Tools like GraphQL Code Generator produce interfaces for every piece of data making your application strongly typed.
Share the same tools
By using GraphQL for client-side state you can reuse already existing tools.
Explore the state of your application
Thanks to Apollo Dev Tools you can see your remote schema stitched together with the client-side part.
Works with other Apollo Links
Loona wraps Apollo Client and has its own Apollo Link. Thanks to that it’s easy to use Loona with other Apollo Links.
We’re open for your feedback!
We’d be happy to hear your feedback so we could improve Loona and figure out the direction of using GraphQL for local state management together, with the whole community, not only React’s but from other frameworks like Angular or Vue.
We also want to collaborate with the Apollo Team on shaping the best way to manage local state with GraphQL and maybe even add parts of our solution into a future Apollo Client release.