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!
TL;DR
- State Management done with GraphQL and Apollo
- We took the best concepts from the Redux and Apollo worlds
- React and Angular
- Website and Documentation
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.
Works out of the box with React Native and NativeScript. It also supports Create React App and Angular CLI (has its own collection of schematics).
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/reactoryarn 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:
Angular version:
Loona, step by step
Now since everything is ready, let’s take a closer look on what Loona does and why.
State
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
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.
Now when BooksList
component asks for data, it gets an empty array.
We also support Higher Order Components. Learn more about queries.
Mutations
When mutation is called, Loona catches that and decides which part reaches the network and which stays on the client.
And here’s how you might update the store. Loona uses Immer
and has few helpers to make writes and updates easier.
Actions
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 AddBook
:
Loona has an Action
and connect
components:
In the example above, we dispatched an action and as the type
says, it should somehow add a new book to the list.
Effect
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.
Updates
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 Query
, Mutation
and Subscription
components of react-apollo
and also provide two helpful Higher Order Components: graphql
and connect
.
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.
Smart store
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 patchQuery
and 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.
Built-in solutions
No need to implement things like Caching or Optimistic UI yourself, it’s part of Apollo and Loona.
Lazy loading
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.
- Website
- GitHub repository
- Examples for React and Angular