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.

Loona lives between the UI and the Apollo Client and intercepts queries and mutations through the Apollo Link.

How to get started

First, you need to install Loona depending on a used framework.

yarn add @loona/react
or
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:

Click to play with Loona and React

Angular version:

Click to play with Loona and Angular

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.

How to query data inside of a component

We used a Query component but Loona also allows to use the HoC.

How to define defaults in a state

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.

How to call a mutation

And here’s how you might update the store. Loona uses Immer and has few helpers to make writes and updates easier.

How to update the store in a mutation
Read more about Mutations on our website

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:

It doesn’t force you to use classes, it might be a plain object.

Loona has an Action and connect components:

How to dispatch an action

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).

UI dispatches an Action and Effects are called. An effect can mutate / update the store or even dispatch another action. UI gets updated too.
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.

Update runs synchronously, every time mutation happens. They work also for remote calls.

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.


Follow us on GitHub (check each person individually) and Medium. we are planning to release many more posts in the next couple of weeks about what we’ve learned using GraphQL in recent years.