Pullstate - Simple hooks-based state management for React
Tags: react, javascript, ionic
Published: 2020-09-21 22:25:53

---
title: Pullstate - Simple hooks-based state management for React
published: true
description: Pullstate is a simple and powerful state management library for React. Perfect for mobile development with Ionic React and Capacitor
tags: react, javascript, ionic
//cover_image: https://direct_url_to_image.jpg
---

State management is one of the most important pieces of an app, and there are a ton of choices for those in the React ecosystem.

In particular, developers building iOS and Android mobile apps with React using [Capacitor](https://capacitorjs.com/) and [Ionic React](http://ionicframework.com/react) often ask us for state management recommendations. Of course, there's Redux, which I remain a [major fan of](https://medium.com/@maxlynch/redux-is-the-pivotal-frontend-innovation-a406736552cb), but also much simpler state management approaches like MobX and [rolling your own using the Context API](https://ionicframework.com/blog/a-state-management-pattern-for-ionic-react-with-react-hooks/).

I've spent a lot of time using Redux and also the bespoke approach with the Context API. Yet, I wasn't satisfied. I wanted to find something that was simple but high performance, and had native integration with Hooks and Function components which I now use exclusively in React (sorry, never want to write the word `class` ever again 😆).

That's when I stumbled on [Pullstate](https://lostpebble.github.io/pullstate/). Pullstate is a small, relatively unknown library (just 300 stars at the time of this writing), but I expect it will become much more popular in time.

## Exploring Pullstate

Pullstate provides a simple Store object that is registered globally, and provides hooks for accessing data from that store in a component:

store.ts:
```typescript
interface StoreType {
  user: User | null;
  currentProject: Project | null;
}

const MyStore = new Store<StoreType>({
 user: null,
 currentProject: null
});

export default MyStore;
```

Then, in your component, simply use the `useState` method provided on the store to select data from the store:

```typescript
const UserProfile: React.FC = () => {
  const user = MyStore.useState(s => s.user);
}
```

## Modifying state

To update state in the store, use the `update` method:

```typescript
const setUser = (user: User) => {
  MyStore.update((s, o) => {
    s.user = user;
  });
}
```

The `update` function works by mutating a Draft of the state. That draft is then processed to produce a new state.

Usually, a state mutation would raise a red flag, but the magic of Pullstate comes from a really interesting project called [Immer](https://github.com/immerjs/immer). Immer essentially proxies an object and then turns mutations on that object into a new object (in my limited experience with it). Sort of how the vdom does diffing to figure out a new DOM tree.

This is incredibly powerful and simple, but does have a few gotcha's. First, reference comparisons on objects in the `s` value above will fail, because they are actually `Proxy` objects. That means doing something like this won't work:

```typescript
MyStore.update(s => {
  s.project = s.projects.find(p => p === newProject)
});
```

Instead, use the second argument, `o` above, which contains the un-proxied original state. Another gotcha is making sure not to return anything from the `update` function.

## Next steps

After having used Pullstate, I will have a hard time not recommending it to all [Ionic React](https://ionicframework.com/react) developers, and those using [Capacitor](https://capacitorjs.com/) with other React UI libraries.

I think Pullstate is a great middle ground between being simple for small projects, but clearly capable of scaling to much more complicated projects. For larger projects multiple stores can be created in parallel, for a sort of redux reducer-inspired organization.

Pullstate also comes with some convenience helpers for [async actions](https://lostpebble.github.io/pullstate/docs/async-actions-introduction) to cut down on async state boilerplate (such as handling success and failure states), though I have not used those extensively yet.

Next on my list is exploring how this might work with something like [reselect](https://github.com/reduxjs/reselect) for building reusable, memoized selectors.

What do you think? Have you used Pullstate? Please share in the comments!