# Quick Start

A no frills, no fuss, introduction the "core" APIs of Easy Peasy. Perfect for the newcomer.

# Create the store

Define your store by providing a plain old JavaScript object based model to the createStore function.

import { createStore } from 'easy-peasy';

const store = createStore({
  todos: [],
});

Note: Your model can be as complex/nested as you like. Feel free to compose it from imports as your state structure scales in complexity.

# Binding the store to your React app

Surround your application with the StoreProvider component, providing it your store instance.

import { StoreProvider } from 'easy-peasy';
import { store } from './store';

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

# Using state in your components

The useStoreState hook allows you to access your store's state.

import { useStoreState } from 'easy-peasy';

function Todos() {
  const todos = useStoreState((state) => state.todos);
  return (
    <ul>
      {todos.map((todo) => (
        <li>{todo.text}</li>
      ))}
    </ul>
  );
}

# Defining actions to perform state updates

Place an action within your model to support updates.

import { createStore, action } from 'easy-peasy';

const store = createStore({
  todos: [],
  addTodo: action((state, payload) => {
    state.todos.push({ text: payload, done: false });
  }),
});

The action will receive the state which is local to it. Update the state by directly mutating the state argument.

Don't worry, under the hood we will convert the operation into an immutable update against your store by using Immer (opens new window).

# Dispatching actions

The useStoreActions hook allows you to access actions from your components.

import { useStoreActions } from 'easy-peasy';

function AddTodoForm() {
  const addTodo = useStoreActions((actions) => actions.addTodo);
  const [value, setValue] = React.useState('');
  return (
    <>
      <input onChange={(e) => setValue(e.target.value)} value={value} />
      <button onClick={() => addTodo(value)}>Add Todo</button>
    </>
  );
}

In this example, we resolve the addTodo action, and bind it to the click of the "Add Todo" button. When the button is clicked it will dispatch the addTodo action, providing the text of the new todo item as the payload.

# Encapsulating side effects via thunks

Thunks provide us with the capability to encapsulate side effects, whilst also having the ability to dispatch actions in order to update our state accordingly.

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

const model = {
  todos: [],
  addTodo: action((state, payload) => {
    state.todos.push(payload);
  }),
  saveTodo: thunk(async (actions, payload) => {
    const { data } = await axios.post('/todos', payload);
    actions.addTodo(data);
  }),
};

Within our thunk we execute an API request. We subsequently utilize the returned data, dispatching the addTodo action to update our state.

# Dispatching thunks within your components

Thunks are accessible in exactly the same manner as actions, i.e. via the useStoreActions hook.

import { useStoreActions } from 'easy-peasy';

function AddTodoForm() {
  const saveTodo = useStoreActions((actions) => actions.saveTodo);
  const [value, setValue] = React.useState('');
  return (
    <>
      <input onChange={(e) => setValue(e.target.value)} value={value} />
      <button onClick={() => saveTodo(value)}>Add Todo</button>
    </>
  );
}

# Deriving state via computed properties

It is a common requirement within state management to need derived state. A basket component might need to derive the total price. A paging component may need the total number of items. There are many cases that can appear as your application evolves.

You can create derived state via the computed API.

import { computed } from 'easy-peasy';

const store = createStore({
  todos: [{ text: 'Learn easy peasy', done: true }],
  completedTodos: computed((state) => state.todos.filter((todo) => todo.done)),
});

Easy Peasy will do all of the required optimization under the hood to keep your computed properties as performant as possible.

# Using computed properties

Computed properties are accessed just like any other state.

import { useStoreState } from 'easy-peasy';

function CompletdTodos() {
  const completedTodos = useStoreState((state) => state.completedTodos);
  return (
    <>
      {completedTodos.map((todo) => (
        <Todo todo={todo} />
      ))}
    </>
  );
}

# Persisting state

Should you wish to persist your state, or part of it, you can utilise the persist API to do so.

import { persist } from 'easy-peasy';

const store = createStore(
  persist({
    count: 1,
    inc: action((state) => {
      state.count += 1;
    }),
  }),
);

This simple adjustment will make Easy Peasy save your store state into sessionStorage. Easy Peasy will persist your state every time it changes.

The next time your store is created Easy Peasy will look for any persisted data and use it to rehydrate your state if it exists.

This process is asynchronous, but you can utilise the useStoreRehydrated hook to make sure the rehydration has completed prior to rendering your components.

import { useStoreRehydrated } from 'easy-peasy';

const store = createStore(persist(model));

function App() {
  const isRehydrated = useStoreRehydrated();
  return isRehydrated ? <Main /> : <div>Loading...</div>;