Quick Start

i.e. The TLDR tutorial 🚀

This quick fire overview will provide you with a brief introduction to 90% of Easy Peasy's API.

Installation

npm install easy-peasy

Use a model to define your store

Your store definition is represented via an object-based model.

const productsModel = {
  items: {
    1: { id: 1, name: 'Peas', price: 10 }
  }
};

const basketModel = {
  productIds: [1]
};

const storeModel = {
  products: productsModel,
  basket: basketModel
};

Feel free to split your model into separate files, importing them and composing them as you please.

Create the store

Provide your model to createStore in order to get a store instance.

import { createStore } from 'easy-peasy';

const store = createStore(storeModel);

FYI, the output store is a Redux store, fully compatible with any library that expects to receive one (e.g. react-redux)

Wrap your application

Use the StoreProvider component to connect the store to your application.

import { StoreProvider } from 'easy-peasy';

ReactDOM.render(
  <StoreProvider store={store}>
    <App />
  </StoreProvider>,
  rootEl
);

Consume state

The useStoreState hook allows you to consume state.

import { useStoreState } from 'easy-peasy';

function ProductsInBasket() {
  const count = useStoreState(state => state.basket.productIds.length);
  return <div>{count} items in basket</div>;
}

Adding actions to your model

Define an action against your model to support updates.

import { action } from 'easy-peasy';
//         👆

const basketModel = {
  productIds: [1],
  //            👇
  addProduct: action((state, payload) => {
    state.productIds.push(payload);
  })
};

The action will receive the state which is local to it.

By default you need to mutate the state with the action, which we convert to an immutable update via immer. If you prefer you can instead return new immutable instances of your state, like a standard Redux reducer.

Dispatching your actions

The useStoreActions hook allows you to access an action within a component.

import { useStoreActions } from 'easy-peasy';
//          👆

function Product({ product }) {
  //                                👇
  const addProductToBasket = useStoreActions(actions => actions.basket.addProduct);
  return (
    <div>
      <h2>{product.name}</h2>
      {/*                            👇                   */}
      <button onClick={() => addProductToBasket(product.id)}>
        Add to basket
      </button>
    </div>
  )
}

Add thunks to perform side effects

Define a thunk in order to perform a side effect, such as making a request to an API.

import { thunk, action } from 'easy-peasy';
//        👆

const productsModel = {
  items: {
    1: { id: 1, name: 'Peas', price: 10 }
  },
  //               👇
  updateProduct: thunk(async (actions, payload) => {
    const updated = await productService.update(payload.id, payload);
    actions.setProduct(updated); // 👈 dispatch local actions to update state
  }),
  setProduct: action((state, payload) => {
    state.items[payload.id] = payload;
  }),
}

Dispatch your thunks

The useStoreActions hook allows you to access a thunk within a component.

import { useStoreActions } from 'easy-peasy';
//           👆

function EditProduct({ product }) {
  //                        👇
  const updateProduct = useStoreActions(actions => actions.products.updateProduct);
  return (
    <ProductForm
      product={product}
      //                            👇
      onSave={updatedValues => updateProduct(updatedValues)}
    />
  );
}

Computed properties

You can create derived state via computed.

import { computed } from 'easy-peasy';
//         👆

const productsModel = {
  items: {
    1: { id: 1, name: 'Peas', price: 10 }
  },
  //        👇
  count: computed(state => Object.values(state.items).length)
}

Consuming computed properties

Computed properties are accessed via the useStoreState hook, just like any other state.

import { useStoreState } from 'easy-peasy';
//            👆

function ProductCount() {
  //               👇
  const count = useStoreState(state => state.products.count);
  return <div>{count} products</div>;
}

Action/Thunk Listeners

You can create listener actions or thunks via the actionOn and thunkOn APIs respectively. These APIs allow you to create an action or thunk that will execute in response to target actions being fired.

import { actionOn } from 'easy-peasy';
//          👆

const auditModel = {
  logs: [],
  onAddedToBasket: actionOn(
    // targetResolver function receives actions and resolves the targets:
    (actions, storeActions) => storeActions.basket.addProduct,
    // action handler that executes when target is executed:
    (state, target) => {
      state.logs(`Added product ${target.payload} to basket`);
    },
  )
}

A listener will receive the same payload as was provided to the target being listened to.

Closing notes

That concludes the quick start overview of Easy Peasy. Whilst this should have provided enough of an overview to immediately begin developing with Easy Peasy we highly recommend that you review the documentation for a more detailed overview of the APIs.

Within the documentation section you will find detailed tutorials, API docs, TypeScript tutorials, recipes, etc.