# Kiss State Management for React - Complete Documentation

*This file can be used by AI agents. It contains the full documentation from https://kissforreact.org/ in a single file.*
*Last updated: 2025-10-27*

## AI Agent Quick Reference Index

*Use this index to quickly locate relevant sections without scanning the entire document.*

### **Document Structure & Quick Navigation**

- [**Overview**](#overview)

- [**Store and state**](#store-and-state)

- [**Components use the state**](#components-use-the-state)

- [**Actions and reducers**](#actions-and-reducers)

- [**Dispatch an action**](#dispatch-an-action)

- [**Components can dispatch actions**](#components-can-dispatch-actions)

- [**Actions can do asynchronous work**](#actions-can-do-asynchronous-work)

- [**Actions can throw errors**](#actions-can-throw-errors)

- [**Components can react to actions**](#components-can-react-to-actions)

- [**Actions can dispatch other actions**](#actions-can-dispatch-other-actions)

- [**Add features to your actions**](#add-features-to-your-actions)
  - [NonReentrant](#nonreentrant)
  - [Retry](#retry)
  - [CheckInternet](#checkinternet)
  - [Debounce (soon)](#debounce-soon)
  - [Throttle (soon)](#throttle-soon)
  - [OptimisticUpdate (soon)](#optimisticupdate-soon)

- [**Persist the state**](#persist-the-state)

- [**Testing your app is easy**](#testing-your-app-is-easy)

- [**Advanced setup**](#advanced-setup)

- [**Advanced action configuration**](#advanced-action-configuration)

- [**Introduction**](#introduction)

- [**Getting Started**](#getting-started)
  - [What is it?](#what-is-it)
  - [Installation](#installation)

- [**Tutorial**](#tutorial)
  - [Creating The State](#creating-the-state)
  - [TodoItem](#todoitem)
  - [TodoList](#todolist)
  - [State](#state)
  - [Setting Up The Store](#setting-up-the-store)
  - [Plain Javascript Obj](#plain-javascript-obj)
  - [State as value objects](#state-as-value-objects)
  - [State as objects with functions](#state-as-objects-with-functions)
  - [Comparison](#comparison)
  - [The Basic Ui](#the-basic-ui)
  - [TodoInput](#todoinput)
  - [ListOfTodos](#listoftodos)
  - [TodoItemComponent](#todoitemcomponent)
  - [RemoveAllButton](#removeallbutton)
  - [Try it yourself](#try-it-yourself)
  - [Sync Actions](#sync-actions)

- [**Synchronous actions**](#synchronous-actions)
  - [AddToAction](#addtoaction)
  - [What if the item already exists?](#what-if-the-item-already-exists)
  - [RemoveAllTodosAction](#removealltodosaction)
  - [Note](#note)
  - [Async Actions](#async-actions)

- [**Asynchronous actions**](#asynchronous-actions)
  - [Add Random Todo](#add-random-todo)
  - [Adding a spinner](#adding-a-spinner)
  - [Combining isWaiting and isFailed](#combining-iswaiting-and-isfailed)
  - [Try it yourself](#try-it-yourself)
  - [Handling Action Errors](#handling-action-errors)
  - [Show error messages in a dialog](#show-error-messages-in-a-dialog)
  - [Showing error messages in components](#showing-error-messages-in-components)
  - [Try it yourself](#try-it-yourself)
  - [How to disable the dialog](#how-to-disable-the-dialog)
  - [Persisting The State](#persisting-the-state)
  - [ClassPersistor](#classpersistor)
  - [Try it yourself](#try-it-yourself)
  - [Implementing your own serializer](#implementing-your-own-serializer)
  - [Testing](#testing)
  - [Basic action testing](#basic-action-testing)
  - [Setting the initial state for the test](#setting-the-initial-state-for-the-test)
  - [Testing asynchronous actions](#testing-asynchronous-actions)
  - [Try it yourself](#try-it-yourself)
  - [Other Improvements](#other-improvements)
  - [Completing and filtering a todo item](#completing-and-filtering-a-todo-item)
  - [Try it yourself](#try-it-yourself)
  - [Full Code](#full-code)
  - [Tutorial code](#tutorial-code)
  - [Conclusion](#conclusion)

- [**Basics**](#basics)
  - [Actions And Reducers](#actions-and-reducers)
  - [The reducer](#the-reducer)
  - [Base action](#base-action)
  - [Actions can have parameters](#actions-can-have-parameters)
  - [Actions can do asynchronous work](#actions-can-do-asynchronous-work)
  - [Actions can throw errors](#actions-can-throw-errors)
  - [Changing State Is Optional](#changing-state-is-optional)
  - [Components Use The State](#components-use-the-state)
  - [useAllState](#useallstate)
  - [useSelect](#useselect)
  - [useObject](#useobject)
  - [Try it out](#try-it-out)
  - [Counter App Examples](#counter-app-examples)
  - [State as a number](#state-as-a-number)
  - [State as a plain JavaScript object](#state-as-a-plain-javascript-object)
  - [State as a class](#state-as-a-class)
  - [State modifies itself](#state-modifies-itself)
  - [Functions calling functions](#functions-calling-functions)
  - [Defining a Base action](#defining-a-base-action)
  - [Testing state and actions](#testing-state-and-actions)
  - [Asynchronous counter](#asynchronous-counter)
  - [Dispatching Actions](#dispatching-actions)
  - [Dispatch (one action)](#dispatch-one-action)
  - [Dispatch all (multiple actions)](#dispatch-all-multiple-actions)
  - [Dispatch and wait](#dispatch-and-wait)
  - [Dispatch and wait all](#dispatch-and-wait-all)
  - [Dispatch sync](#dispatch-sync)
  - [Actions can dispatch other actions](#actions-can-dispatch-other-actions)
  - [Hooks To Dispatch Actions](#hooks-to-dispatch-actions)
  - [useDispatch etc](#usedispatch-etc)
  - [useStore](#usestore)
  - [Provide The Store](#provide-the-store)
  - [Try it out](#try-it-out)
  - [Store And State](#store-and-state)
  - [Initial state](#initial-state)
  - [Choice](#choice)
  - [Immutable state](#immutable-state)
  - [User Exceptions](#user-exceptions)
  - [Example](#example)
  - [Error queue](#error-queue)
  - [Show error messages in a dialog](#show-error-messages-in-a-dialog)
  - [Wait Fail Succeed](#wait-fail-succeed)
  - [In actions](#in-actions)
  - [In components](#in-components)
  - [Show a spinner](#show-a-spinner)
  - [Show an error message](#show-an-error-message)
  - [Combining isWaiting and isFailed](#combining-iswaiting-and-isfailed)

- [**Advanced Actions**](#advanced-actions)
  - [Aborting The Dispatch](#aborting-the-dispatch)

- [**Example**](#example)
  - [Creating a base action](#creating-a-base-action)
  - [Action Features](#action-features)
  - [nonReentrant](#nonreentrant)
  - [retry](#retry)
  - [checkInternet](#checkinternet)
  - [debounce](#debounce)
  - [throttle](#throttle)
  - [ignoreOld](#ignoreold)
  - [optimisticUpdate](#optimisticupdate)
  - [Action Status](#action-status)
  - [isCompleted](#iscompleted)
  - [isCompletedOk](#iscompletedok)
  - [isCompletedFailed](#iscompletedfailed)
  - [Getting the action error](#getting-the-action-error)
  - [Up until which point did the action run?](#up-until-which-point-did-the-action-run)
  - [Use cases](#use-cases)
  - [Base Action With Common Logic](#base-action-with-common-logic)

- [**Common logic**](#common-logic)
  - [Before And After The Reducer](#before-and-after-the-reducer)
  - [Before](#before)
  - [After](#after)
  - [Example](#example)
  - [Creating a base action](#creating-a-base-action)
  - [Errors Thrown By Actions](#errors-thrown-by-actions)
  - [What happens](#what-happens)
  - [Local error handling](#local-error-handling)
  - [Showing a dialog to the user](#showing-a-dialog-to-the-user)
  - [Creating a base action](#creating-a-base-action)
  - [Global error handling](#global-error-handling)
  - [Disabling errors](#disabling-errors)
  - [Error observer](#error-observer)
  - [UserExceptionAction](#userexceptionaction)
  - [Wrapping The Reducer](#wrapping-the-reducer)
  - [Example](#example)
  - [Creating a base action](#creating-a-base-action)

- [**Testing**](#testing)
  - [Debugging The App](#debugging-the-app)
  - [Viewing the state](#viewing-the-state)
  - [Checking actions in progress](#checking-actions-in-progress)
  - [State changes](#state-changes)
  - [Logging state changes to the console](#logging-state-changes-to-the-console)
  - [Dispatch count](#dispatch-count)
  - [Mocking Actions](#mocking-actions)
  - [mocks.add](#mocksadd)
  - [mocks.remove](#mocksremove)
  - [mocks.clear](#mocksclear)
  - [Testing The App](#testing-the-app)

- [**Dispatch, wait and expect**](#dispatch-wait-and-expect)
  - [Testing steps](#testing-steps)
  - [How about dispatch and dispatchAll?](#how-about-dispatch-and-dispatchall)
  - [Waiting for conditions](#waiting-for-conditions)
  - [Recording](#recording)
  - [Testing User Exceptions](#testing-user-exceptions)

- [**Testing UserExceptions**](#testing-userexceptions)
  - [Checking the error queue](#checking-the-error-queue)

- [**Miscellaneous**](#miscellaneous)
  - [Business Logic](#business-logic)
  - [Example](#example)
  - [Log And Metrics](#log-and-metrics)
  - [actionObserver](#actionobserver)
  - [stateObserver](#stateobserver)
  - [Try it yourself](#try-it-yourself)
  - [Persistor](#persistor)
  - [Setup](#setup)
  - [Implementation](#implementation)
  - [ClassPersistor](#classpersistor)
  - [App lifecycle](#app-lifecycle)
  - [Log out](#log-out)
  - [Manually accessing the persistor](#manually-accessing-the-persistor)
  - [Slices](#slices)
  - [How to](#how-to)
  - [Why slices may not be necessary](#why-slices-may-not-be-necessary)
  - [Easy state access without slices](#easy-state-access-without-slices)
  - [Undo And Redo](#undo-and-redo)
  - [Try it yourself](#try-it-yourself)
  - [Wait For Condition](#wait-for-condition)
  - [waitCondition](#waitcondition)
  - [waitAllActions](#waitallactions)
  - [waitAllActionTypes and waitActionType](#waitallactiontypes-and-waitactiontype)
  - [waitAnyActionTypeFinishes](#waitanyactiontypefinishes)
  - [waitActionCondition](#waitactioncondition)
  - [Optional parameters](#optional-parameters)

### **API & Keywords Quick Reference**
```
Hooks: useAllState, useClearExceptionFor, useDispatch, useDispatchAll, useDispatchAndWait, useDispatchAndWaitAll, useExceptionFor, useIsFailed, useIsWaiting, useObject, useSelect
Methods: createStore, dispatch, dispatchAll, dispatchAndWait, dispatchAndWaitAll, reduce, waitCondition
Features: checkInternet, debounce, nonReentrant, optimisticUpdate, retry, throttle
```

---

# Overview

Kiss State is a powerful and modern state management library for React, launched in **May 2025**.
While new to React, Kiss has been available for Flutter for years, battle-tested in hundreds of 
real-world applications.

**AI ready:** Centralizes state and business logic in predictable ways, 
making it easier for AI models to reason about code. 
This improves AI-driven generation, 
and helps you achieve results with surprisingly little effort.

**[📄 Complete AI Documentation](/kiss-state-management-docs.md)** - 
*Single file containing all Kiss documentation, optimized for AI agents. Just copy the link and paste it to any AI.*

&nbsp;

# Store and state

The **store** holds all the application **state**. A few examples:

```tsx
// If your state is a number
const store = createStore<number>({initialState: 1});
```

```tsx
// If your state is a plain JavaScript object
const store = createStore({initialState: {name: 'Mary', age: 25}});
```

```tsx
// If your state is an ES6 class object
class State { constructor(public name: string, public age: number){} }
const store = createStore<State>({initialState: new State('Mary', 25)});
```

&nbsp;

Use a `StoreProvider` to add the store to your component tree:

```tsx
function App() {
  return (
    <StoreProvider store={store}>
      <AppContent />
    </StoreProvider>
  );
};
```

&nbsp;

# Components use the state

The `useAllState` hook lets you access the state from any component.
It rebuilds when the state changes.

```tsx
function MyComponent() { 
  const state = useAllState();   
  
  return <div>{state.name} is {state.age} years old</div>;    
};
```

&nbsp;

The `useSelect` hook selects only the part of the state that your component needs.
It rebuilds only when that part changes.

```tsx
function MyComponent() { 
  const name = useSelect((state) => state.name);   
  const age = useSelect((state) => state.age);
     
  return <div>{name} is {age} years old</div>;    
};
```

&nbsp;

The `useObject` hook also only rebuilds when needed:

```tsx
function MyComponent() {
 
  const state = useObject((state) => {
    name: state.name, 
    age: state.age
  });
       
  return <div>{state.name} is {state.age} years old</div>;    
};
```

&nbsp;

# Actions and reducers

An **action** is a class that contains its own **reducer** function. 
This reducer has access to the current state, and its goal is to return a new and modified state.

```tsx
class Increment extends Action {

  reduce() { 
    // The reducer has access to the current state
    return this.state + 1; // It returns a new state 
  };
}
```

&nbsp;

# Dispatch an action

The store state is **immutable**.

The only way to change the store state is by dispatching an **action**.
This will run the action's reducer to get a new state that will replace the current one,
and rebuild all components that use it.

```tsx
// Dispatch an action
store.dispatch(new Increment());

// Dispatch multiple actions
store.dispatchAll([new Increment(), new LoadText()]);

// Dispatch an action and wait for it to finish
await store.dispatchAndWait(new Increment());

// Dispatch multiple actions and wait for them to finish
await store.dispatchAndWaitAll([new Increment(), new LoadText()]);
```

&nbsp;

# Components can dispatch actions

The hooks to dispatch actions are `useDispatch`, `useDispatchAll` etc.

```tsx
function MyComponent() { 
  const dispatch = useDispatch();  

  return (
    <Button onClick={() => dispatch(new LoadText())}> 
      Click me! 
    </Button>
  );
};
```

&nbsp;

Or getting the store with `useStore` also allows you to dispatch actions:

```tsx
function MyComponent() { 
  const store = useStore();  

  return (
    <Button onClick={() => store.dispatch(new LoadText())}> 
      Click me! 
    </Button>
  );
};
```

&nbsp;

# Actions can do asynchronous work

Actions can download information from the internet, or do any other async work.

```tsx
const store = createStore<string>({initialState: ''});
```

```tsx
class LoadText extends Action {

  // This reducer returns a Promise
  async reduce() {

    // Download something from the internet
    let response = await fetch('https://dummyjson.com/todos/1');
    let text = await response.text(); 

    // Change the state with the downloaded information
    return (state) => text;
  }
}
```

&nbsp;

# Actions can throw errors

If something bad happens, you may simply **throw an error**. In this case, the state will not
change. Errors are caught globally and can be handled in a central place, later.

In particular, if you throw a `UserException`, which is a type provided by Kiss,
a dialog (or other UI) will open automatically, showing the error message to the user.

```tsx
class LoadText extends Action {
  
  async reduce() {
    let response = await fetch("https://dummyjson.com/todos/random/1");
    if (!response.ok) throw new UserException("Failed to load.");    
    
    let text = await response.text();     
    return (state) => text;
  }
}
```

&nbsp;

# Components can react to actions

To show a spinner while an asynchronous action is running, use `isWaiting(action)`.

To show an error message inside the component, use `isFailed(action)`.

```tsx
function MyComponent() {

  const isWaiting = useIsWaiting(LoadText); 
  const isFailed = useIsFailed(LoadText);  
  const state = useAllState();  
  
  if (isWaiting) return <CircularProgress />
  if (isFailed) return <p>Loading failed...</p>;
  return <p>{state}</p>;
}
```

&nbsp;

# Actions can dispatch other actions

You can use `dispatchAndWait` to dispatch an action and wait for it to finish.

```tsx
class LoadTextAndIncrement extends Action {

  async reduce() {
  
    // Dispatch and wait for the action to finish   
    await this.dispatchAndWait(new LoadText());
    
    // Only then, increment the state
    return (state) => state.copy({ count: state.count + 1 });  
  }
}
```

&nbsp;

You can also dispatch actions in **parallel** and wait for them to finish:

```tsx
class BuyAndSell extends Action {

  async reduce() {  
    
    // Dispatch and wait for both actions to finish
    await this.dispatchAndWaitAll([
      new BuyAction('IBM'), 
      new SellAction('TSLA')
    ]);        

    return (state) => state.copy({ 
      message: `New cash balance is ${this.state.cash}` 
    });
  }
}
```

&nbsp;

You can also use `waitCondition` to wait until the `state` changes in a certain way:

```tsx
class SellStockForPrice extends Action {
  constructor(public stock: string, public price: number) { super(); }

  async reduce() {
  
    // Wait until the stock price is higher than the limit price
    await this.waitCondition(
      (state) => state.stocks.getPrice(this.stock) >= this.price
    );
    
    // Only then, post the sell order to the backend
    let amount = await postSellOrder(this.stock);    
    
    return (state) => 
      state.copy({
        stocks: state.stocks.setAmount(this.stock, amount),
      });
  }
}
```

&nbsp;

# Add features to your actions

It's easy to add your own reusable "features" to your actions,
but they come out of the box with some interesting ones:

## NonReentrant

To prevent an action from being dispatched while it's already running,
add the `nonReentrant` property to your action class and set it to `true`.

```tsx
class LoadText extends Action { 
  nonReentrant = true;
   
  reduce() { ... }
}
```

&nbsp;

## Retry

To retry an action a few times with exponential backoff, if it fails,
add the `retry` property to your action class.

```tsx
class LoadText extends Action {   
  retry = {on: true}
         
  reduce() { ... }
}
```

&nbsp;

And you can specify the retry policy:

```tsx
class LoadText extends Action {

  retry = {
    initialDelay: 350, // Milliseconds delay before the first attempt
    maxRetries: 3,     // Number of retries before giving up
    multiplier: 2,     // Delay increase factor for each retry
    maxDelay: 5000,    // Max milliseconds delay between retries
  }
   
  reduce() { ... }
}
```

&nbsp;

## CheckInternet

To check if there is internet before running the action, add the `checkInternet` property.
If there's no internet, the action aborts. Optionally, show a dialog to the user
saying: "There is no Internet, please verify your connection".

```tsx
class LoadPrices extends Action {    
  checkInternet = { dialog: true } 
   
  async reduce() { ... } 
}
```

&nbsp;

## Debounce (soon)

To limit how often an action occurs in response to rapid inputs, you can add a `debounce` property
to your action class. For example, when a user types in a search bar, debouncing ensures that not
every keystroke triggers a server request. Instead, it waits until the user pauses typing before
acting.

```tsx
class SearchText extends Action {
  constructor(public searchTerm: string) { super(); }
  
  debounce = 300 // Milliseconds
   
  async reduce()  {      
    let result = await loadJson('https://example.com/?q=', searchTerm);
    return (state) => state.copy({searchResult: result});
  }   
}
```

&nbsp;

## Throttle (soon)

To prevent an action from running too frequently, you can add a `throttle` property to your
action class. This means that once the action runs it's considered _fresh_, and it won't run
again for a set period of time, even if you try to dispatch it.
After this period ends, the action is considered _stale_ and is ready to run again.

```tsx
class LoadPrices extends Action {    
  throttle = 5000 // Milliseconds
   
  async reduce()  {      
    let result = await loadJson('https://example.com/prices');
    return (state) => state.copy({prices: result});
  } 
}
```

&nbsp;

## OptimisticUpdate (soon)

To provide instant feedback on actions that save information to the server, this feature immediately
applies state changes as if they were already successful, before confirming with the server.
If the server update fails, the change is rolled back and, optionally, a notification can inform
the user of the issue.

```tsx
class SaveName extends Action {    
  optimisticUpdate = { ... } 
   
  async reduce() { ... } 
}
```

&nbsp;

# Persist the state

You can add a `persistor` to save the state to the local device disk.
It supports serialization of JavaScript objects **and** ES6 classes.

```tsx
const store = createStore<State>({  
  persistor: new Persistor(),
});  
```

&nbsp;

# Testing your app is easy

Just dispatch actions and wait for them to finish.
Then, verify the new state or check if some error was thrown.

```tsx
class State {
  constructor(
    public items: string[], 
    public selectedItem: number
  ) {}
}

test('Selecting an item', async () => {

  const store = createStore<State>({      
    initialState: new State(['A', 'B', 'C'], -1)
  });
  
  // Should select item 2
  await store.dispatchAndWait(new SelectItem(2));
  expect(store.state.selectedItem).toBe('B');
  
  // Fail to select item 42
  let status = await store.dispatchAndWait(new SelectItem(42));    
  expect(status.originalError).toBeInstanceOf(UserException);          
});
```

&nbsp;

# Advanced setup

If you are the Team Lead, you set up the app's infrastructure in a central place,
and allow your developers to concentrate solely on the business logic.

You can add a `stateObserver` to collect app metrics, an `errorObserver` to log errors,
an `actionObserver` to print information to the console during development,
and a `globalWrapError` to catch all errors.

```tsx
const store = createStore<string>({    
  stateObserver: (action, prevState, newState, error, count) => { ... },
  errorObserver: (error, action, store) => { ... },
  actionObserver: (action, count, ini) => { ... },
  globalWrapError: (error) => { ... }
});  
```

&nbsp;

For example, here we handle `FirestoreError` errors thrown by Firebase.
We convert them into `UserException` errors, which are built-in types that
automatically show a message to the user in an error dialog:

```tsx
globalWrapError: (error: any) => {
   return (error instanceof FirestoreError)
      ? new UserException('Error connecting to Firebase')
      : error;
   }  
```

&nbsp;

# Advanced action configuration

The Team Lead may create a base action class that all actions will extend, and add some common
functionality to it. For example, getter shortcuts to important parts of the state,
and selectors to help find information.

```tsx
class State {  
  items: Item[];    
  selectedItem: number;
}

export abstract class Action extends KissAction<State> {

  // Getter shortcuts   
  get items() { return this.state.items; }
  get selectedItem() { return this.state.selectedItem; }
  
  // Selectors 
  findById(id) { return this.items.find((item) => item.id === id); }
  get selectedIndex() { return this.items.indexOf(this.selectedItem); }
  searchByText(text) { return this.items.find((item) => item.text.includes(text)); }
}
```

&nbsp;

Now, all actions can use them to access the state in their reducers:

```tsx
class SelectItem extends Action {
  constructor(public id: number) { super(); }

  reduce() {
    let item = this.findById(this.id);
    if (item === undefined) throw new Error('Item not found');
    return this.state.copy({selectedItem: item});
  }
}
```

---

# Introduction

# Getting Started

Kiss is:

* Simple to learn and easy to use
* Powerful enough to handle complex applications with millions of users
* Testable

This means you'll be able to create web and mobile apps much faster,
and other people on your team will easily understand and modify your code.

## What is it?

Kiss (Keep It Simple State) is a modern JavaSript/TypeScript state management library for
React, created by [Marcelo Glasberg](https://github.com/marcglasberg), and launched in May 2025.

While new to React, Kiss is a mature solution,
having been [available for Flutter](https://pub.dev/packages/async_redux) with a different name for a few years, meaning its features have been battle-tested in hundreds of real-world applications.

## Installation

```npm
npm install kiss-for-react
```

```yarn
yarn add kiss-for-react
```

Next, let's follow a short tutorial to see how easy it is to use Kiss.
We'll create a simple _Todo List_ app.

---

# Tutorial

## Creating The State

Our Todo app state will be composed of 3 data structures, named as follows:

* `TodoItem` represents a single todo item
* `TodoList` represents a list of `TodoItem`s
* `State` is the store state, which contain the `TodoList`

These can be plain JavaScript objects, but also ES6 classes.

I'll use classes in this page, 
but will also show the code with **[plain objects](./plain-javascript-obj)** at 
the end of this tutorial.

## TodoItem

The `TodoItem` class represents a single todo item,
with some `text` and a `completed` status, which starts as false (not completed):

```tsx title="TodoItem.ts"
export class TodoItem {
  constructor(
    public text: string,
    public completed: boolean = false) { 
  }   
}
```

We'll add to it a `toggleCompleted()` function, which returns a copy of the item
with the same text, but opposite completed status:

```tsx title="TodoItem.ts"
export class TodoItem {
  constructor(
    public text: string,
    public completed: boolean = false) { 
  }   
  
  toggleCompleted() {
    return new TodoItem(this.text, !this.completed);
  }
}
```

This class is **immutable**, as it doesn't have any setters, and its single
function `toggleCompleted` returns a new `TodoItem` object, instead of modifying the current one.

## TodoList

The `TodoList` class is a simple list of todo items of type `TodoItem`:

```tsx title="TodoList.ts"
export class TodoList {  
  constructor(public readonly items: TodoItem[] = []) {}  
}
```

We can add of sorts of functions to the `TodoList` class, which will later help us manage the list
of todos. These are a few examples:

* `addTodoFromText` - Add a new todo item to the list from a text string.
* `addTodo` - Add a new todo item to the list.
* `ifExists` - Check if a todo item with a given text already exists.
* `removeTodo` - Remove a todo item from the list.
* `toggleTodo` - Toggle the completed status of a todo item.
* `isEmpty` - Check if there are no todos that appear when a filter is applied.
* `iterator` - Allow iterating over the list of todos.
* `toString` - Return a string representation of the list of todos.
* `empty` - A static empty list of todos.

Here is the full code of the `TodoList` class, with all the above functions implemented:

```tsx title="TodoList.ts"
export class TodoList {  
  constructor(public readonly items: TodoItem[] = []) {}  
  
  addTodoFromText(text: string): TodoList {
    const trimmedText = text.trim();
    const capitalizedText = trimmedText.charAt(0).toUpperCase() + trimmedText.slice(1);
    return this.addTodo(new TodoItem(capitalizedText));
  }
  
  addTodo(newItem: TodoItem): TodoList {
    if ((newItem.text === '') || this.ifExists(newItem.text))
      return this;
    else
      return new TodoList([newItem, ...this.items]);
  }
  
  ifExists(text: string): boolean {
    return this.items.some((todo) => todo.text.toLowerCase() === text.toLowerCase());
  }
  
  removeTodo(item: TodoItem): TodoList {
    return new TodoList(this.items.filter(itemInList => itemInList !== item));
  }
  
  toggleTodo(item: TodoItem): TodoList {
    const newTodos = this.items.map(itemInList => (itemInList === item) ? item.toggleCompleted() : itemInList);
    return new TodoList(newTodos);
  }   
  
  isEmpty() {
    return this.items.length === 0;
  }

  * [Symbol.iterator]() {
    for (let i = 0; i < this.items.length; i++) {
      yield this.items[i];
    }
  }

  toString() { return `TodoList{${this.items.join(',')}}`; }
  
  static empty: TodoList = new TodoList();
}
```

Note again that all functions above return new `TodoList` objects,
instead of modifying the current one.
This means `TodoList` is **immutable**.

Also note that all these functions are easy to create, and it would also be easy to create unit
tests for them.

Adding these functions to the `TodoList` class will allow us to manage the immutable list of
todos in a clean and efficient way, without resorting to external "immutable state libraries"
like [Immer](https://www.npmjs.com/package/immer).

## State

Finally, we need to define the store state. In the future, we may want to add a lot of
different things to the state, but for now we'll keep it simple
and just add the `TodoList` to it:

```tsx title="State.ts"
export class State {
  todoList: TodoList;

  constructor({ todoList }: { todoList: TodoList }) {
    this.todoList = todoList;
  }

  withTodoList(todoList: TodoList): State {
    return new State({ todoList: todoList });
  }

  static initialState: State = new State({ todoList: TodoList.empty });
}
```

Note the state class above has a `withTodoList()` function that returns a copy of the state,
but replacing the current list of todos with a new one. This is an **immutable** operation,
as it creates a new state object.

We also defined a static variable called `initialState`. That's optional, but common.
It's just a default state that can be used when the store is created.
For example, **instead** of:

```tsx
const store = createStore<State>({
  initialState: new State({todoList: TodoList.empty}),  
});
```

We can now write:

```tsx
const store = createStore<State>({
  initialState: State.initialState,
});
```

## Setting Up The Store

This tutorial's goal is to show how easy it is to use Kiss.

We'll create a simple _Todo List_ app, with a list of todos and a button to add a new todo.

Start by declaring `App` as the root React component:

```tsx title="main.tsx"
import React from 'react'
import { createRoot } from 'react-dom/client'
import { App } from './App'

const container = document.getElementById('root');
const root = createRoot(container!);
root.render(<React.StrictMode><App/></React.StrictMode>);
```

```tsx title="index.js"
import { AppRegistry } from 'react-native';
import { App } from './src/App';

AppRegistry.registerComponent("TodoAppReactNative", () => App);
```

<br></br>

We then create the Kiss **store**, and make it available:

```tsx title="App.tsx"
import React from "react";
import { Store, StoreProvider } from 'kiss-for-react';

const store = createStore<State>({
  initialState: State.initialState,
});

export const App: React.FC = () => {
  return (
    <StoreProvider store={store}>
      <AppContent />
    </StoreProvider>
  );
}
```

## Plain Javascript Obj

In the previous sections, we used [ES6 classes to create the state](./full-code#tutorial-code).

If you'd prefer to use **plain JavaScript (or TypeScript) objects**, keep reading.
I'll show you how to create the state using _value objects_ or _objects with functions_.

## State as value objects

We will define our todo-items as simple value objects containing a string and a boolean.
The state will consist of a list of these todo items and a filter.

```ts
// A single todo item.
export interface TodoItem {
  text: string;
  completed: boolean;
}

export enum Filter {
  showAll = "Showing ALL",
  showCompleted = "Showing COMPLETED",
  showActive = "Showing ACTIVE",
}

// The app state
export interface State {
  todoList: TodoItem[];
  filter: Filter;
}
```

We can then create the store like this:

```ts
const store = createStore<State>({
  initialState: {
    todoList: [],
    filter: Filter.showAll,
  }
});
```

Since the state is now so simple,
the **actions** need to be rewritten to transform the state.
In other words, we need to put "business code" inside the actions.
For example:

```ts
class AddTodoAction extends Action {
  constructor(readonly text: string) { super(); }

  reduce() {    

    let ifExists = this.state.todoList.some(
      (todo) => todo.text.toLowerCase() === this.text.toLowerCase()
    );

    if (ifExists) {
      throw new UserException(`The item "${this.text}" already exists.`, {
        errorText: `Type something else other than "${this.text}"`,
      });
    }

    const capitalizedText = this.text.trim().charAt(0).toUpperCase() + this.text.trim().slice(1);
    let newItem = { text: capitalizedText, completed: false };
    let newTodoList = [newItem, ...this.state.todoList];

    return { 
      todoList: newTodoList, 
      filter: this.state.filter 
    };
  }
}
```

This is the complete code with value objects:

**[Live Example](https://codesandbox.io/s/k543sv)**

<br></br>

## State as objects with functions

If we want to avoid adding "business code" to the actions,
we can instead add **functions** to the objects.
We want objects to know how to modify themselves.

For example, this would be our `TodoList` code:

```ts
export interface TodoList {
  items: TodoItem[];
  addTodoFromText: (text: string) => TodoList;
  addTodo: (newItem: TodoItem) => TodoList;
  ifExists: (text: string) => boolean;
  removeTodo: (item: TodoItem) => TodoList;
  removeCompleted: () => TodoList;
  toggleTodo: (item: TodoItem) => TodoList;
  isEmpty: () => boolean;
  countCompleted: () => number;
  [Symbol.iterator]: () => IterableIterator<TodoItem>;
  toString: () => string;
}

const createTodoList = (items: TodoItem[] = []): TodoList => ({
  items,
  
  addTodoFromText(text: string) {
    const trimmedText = text.trim();
    const capitalizedText =
      trimmedText.charAt(0).toUpperCase() + trimmedText.slice(1);
    return this.addTodo(createTodoItem(capitalizedText));
  },
  
  addTodo(newItem: TodoItem) {
    if (newItem.text === "" || this.ifExists(newItem.text)) return this;
    else return createTodoList([newItem, ...this.items]);
  },
  
  ifExists(text: string) {
    return this.items.some(
      (todo) => todo.text.toLowerCase() === text.toLowerCase()
    );
  },
  
  removeTodo(item: TodoItem) {
    return createTodoList(
      this.items.filter((itemInList) => itemInList !== item)
    );
  },
  
  removeCompleted() {
    return createTodoList(
      this.items.filter((itemInList) => !itemInList.completed)
    );
  },
  
  toggleTodo(item: TodoItem) {
    const newTodos = this.items.map((itemInList) =>
      itemInList === item ? item.toggleCompleted() : itemInList
    );
    return createTodoList(newTodos);
  },
  
  isEmpty() {
    return this.items.length === 0;
  },
  
  countCompleted() {
    return this.items.filter((item) => item.completed).length;
  },
  
  *[Symbol.iterator]() {
    for (let i = 0; i < this.items.length; i++) {
      yield this.items[i];
    }
  },
  
  toString() {
    return `TodoList{${this.items.join(",")}}`;
  },
});
```

The code above is similar to [the one](./full-code#tutorial-code) we created with ES6 classes.
Now the actions don't need to know how to transform the state.
Instead, they ask the state to modify itself:

```ts
class AddTodoAction extends Action {
  constructor(readonly text: string) {
    super();
  }

  reduce() {    

    if (this.state.todoList.ifExists(this.text)) {
      throw new UserException(`The item "${this.text}" already exists.`, {
        errorText: `Type something else other than "${this.text}"`,
      });
    }

    let newTodoList = this.state.todoList.addTodoFromText(this.text);
    return this.state.withTodoList(newTodoList);
  }
}
```

This is the complete code with objects with functions:

**[Live Example](https://codesandbox.io/s/2cwkjc)**

## Comparison

Let's compare the 3 approaches:

* [State as ES6 classes](./full-code#tutorial-code) - The business code is inside the state classes.
  Actions don't need to contain any business code. Easy to serialize with the `ClassPersistor`
  provided by Kiss. Immutability is trivial.

* [State as value objects](#state-as-value-objects) - The business code is inside the actions.
  Easy to serialize with `JSON.stringify` and `JSON.parse`.
  May benefit from [Immer](https://www.npmjs.com/package/immer) to help with immutability.

* [State as objects with functions](#state-as-objects-with-functions) - The business code is inside
  the state objects.
  The actions don't need to contain any business code. Immutability is trivial.
  More difficult to serialize, since the deserialization process must preserve the functions.

In table format:

| Approach                                                            | Business Code Location   | Serialization                               | Immutability                                                  |
|---------------------------------------------------------------------|--------------------------|---------------------------------------------|---------------------------------------------------------------|
| [State as ES6 classes](./full-code#tutorial-code)                   | Inside the state classes | Easy with `ClassPersistor` from Kiss | Trivial                                                       |
| [State as value objects](#state-as-value-objects)                   | Inside the actions       | Easy with `JSON.stringify` and `JSON.parse` | May benefit from [Immer](https://www.npmjs.com/package/immer) |
| [State as objects with functions](#state-as-objects-with-functions) | Inside the state objects | More difficult, must preserve functions     | Trivial                                                       |

## The Basic Ui

The app UI contains:

* A **title**
* An **input** to add more todo items
* A **list** of todos
* A **button** to remove all todo items

```tsx 
function AppContent() {
  return (
    <div>
      <h1>Todo List</h1>
      <TodoInput />
      <ListOfTodos />        
      <RemoveAllButton />
    </div>
  );
};
```

```tsx 
function AppContent() {
  return (
    <View>
      <Text>Todo List</Text>
      <TodoInput />
      <ListOfTodos />
      <RemoveAllButton />
    </View>
  );
};
```

<br></br>

**[Live Example](https://codesandbox.io/s/cgq5rs)**

<br></br>
<br></br>

Let's detail below the components: `TodoInput`, `ListOfTodos` and `RemoveAllButton`.

## TodoInput

The `TodoInput` component is a simple input field that allows the user to type a new todo item,
and then press `Enter` or click a button to add it to the list.

```tsx 
function TodoInput() {

  const [inputText, setInputText] = useState<string>('');
  
  const store = useStore(); 
  
  async function processInput(text: string) {
    let status = await store.dispatchAndWait(new AddTodoAction(text))
    if (status.isCompletedOk) setInputText(''); 
  }
  
  return (
    <div>           
      <input 
         placeholder="Type here..."        
         value={inputText} maxLength={50} 
         onChange={(e) => { setInputText(e.target.value); }}
         onKeyDown={(e) => { if (e.key === "Enter") processInput(inputText); }}
      />
      
      <button onClick={() => processInput(inputText)}>
        Add
      </button>
      
    </div>
  );
};
```

```tsx
function TodoInput() {

  const [inputText, setInputText] = useState<string>('');
    
  const store = useStore();
  
  async function processInput(text: string) {
    let status = await store.dispatchAndWait(new AddTodoAction(text));
    if (status.isCompletedOk) setInputText(''); 
  }

  return (
    <View>          
      <TextInput
         placeholder={'Type here...'}
         value={inputText}          
         onChangeText={(text) => { setInputText(text); }}
         onSubmitEditing={() => processInput(inputText)}
      />

      <TouchableOpacity onPress={() => processInput(inputText)}>
        <Text>Add</Text>
      </TouchableOpacity>
      
    </View>          
  );
};
```

As you can see above, the `processInput` function is called whenever the user presses `Enter`
or clicks the "Add" button. This function uses the `useStore` hook to get a reference to the store,
and then dispatches an `AddTodoAction` with the input text.

A simplified version of the `processInput` could simply use the store's `dispatch`
method:

```tsx
async function processInput(text: string) {
  store.dispatch(new AddTodoAction(text));     
}
```

However, when the action succeeds, we want to clear the input field. To do this, we need
to **wait** until the action is completed, check if it completed **successfully**, and then clear
the input field with `setInputText('')`.

This is why instead of `dispatch` we want to use the `dispatchAndWait` method.
It returns a `Promise` that completes when the action is completed.
If we await it we get a status of type `ActionStatus`
that tells us if the action was successful or not.

In other words, we get the `status`,
and if `status.isCompletedOk` is true we can clear the input field:

```tsx
async function processInput(text: string) {
  let status = await store.dispatchAndWait(new AddTodoAction(text));
  if (status.isCompletedOk) setInputText(''); 
}
```

## ListOfTodos

The list of todos uses the `useSelect` hook to get the list of todos from `state.todoList.items`,
and then maps over them to render each todo item component.

```tsx
function ListOfTodos() {
  const todoItems = useSelect((state) => state.todoList.items);

  return (
    <div className="listOfTodos">
      {todoItems.map((item, index) => (
        <TodoItemComponent key={index} item={item} />
      ))}
    </div>
  );
}
```

## TodoItemComponent

For the moment, the todo item component is just the text of the item.

```tsx
function TodoItemComponent({item}: {item: TodoItem}) {
  return <div>{item.text}</div>;
}
```

## RemoveAllButton

Finally, we add a button that uses the `useStore` hook to get a reference to the store,
and then uses it to dispatch a `RemoveAllTodosAction` when clicked.

```tsx
function RemoveAllButton() {
  const store = useStore();
  return (
    <button onClick={() => store.dispatch(new RemoveAllTodosAction())}>
      Remove All
    </button>
  );
}
```

## Try it yourself

Type "Buy milk" in the input, and press the `Add` button or the `Enter` key.
Try adding other todo items.
Then remove all of them by clicking the `Remove All` button.

To see all files in this project,
click the "hamburger icon" in the top left corner of the code editor.
The state classes are in file `State.ts`,
while the store, actions and components are in the `App.tsx` file.

**[Live Example](https://codesandbox.io/s/cgq5rs)**

## Sync Actions

# Synchronous actions

A **synchronous** action is a type of action that doesn't involve any asynchronous operations.

It cannot involve any network requests, file system operations, or any other kind of asynchronous
operation.

## AddToAction

As we've seen, when the user types a new todo item in the `TodoInput` component and
presses `Enter`, the app dispatches the `AddToAction` action:

```tsx
store.dispatch(new AddTodoAction(text));
```

The action payload is the `text` of the new todo item:

```tsx
class AddTodoAction extends Action {
  constructor(readonly text: string) { super(); }
}
```

All actions must also contain a `reduce()` function,
which has access to the current state of the app and returns a new state.

In our example, this new state will have **current the todo list** with the **new todo item** added
to it.

The current todo list is readily available in the `reduce()` function
through `this.state.todoList`:

```tsx
class AddTodoAction extends Action {
  constructor(readonly text: string) { super(); }

  reduce() {  
    let currentTodoList = this.state.todoList;
    ...
  }
}
```

Since the current todo list is of type `TodoList`,
we can then use all functions from the `TodoList` class.
Let's [recap](./creating-the-state#todolist) the functions we've made available in that class:

* `addTodoFromText` - Add a new todo item to the list from a text string.
* `addTodo` - Add a new todo item to the list.
* `ifExists` - Check if a todo item with a given text already exists.
* `removeTodo` - Remove a todo item from the list.
* `toggleTodo` - Toggle the completed status of a todo item.
* `isEmpty` - Check if there are no todos that appear when a filter is applied.
* `iterator` - Allow iterating over the list of todos.
* `toString` - Return a string representation of the list of todos.
* `empty` - A static empty list of todos.

One of these functions is `addTodoFromText()`, which adds a new todo item to the list.
Exactly what we want.

This is the resulting action code:

```tsx
class AddTodoAction extends Action {
  constructor(readonly text: string) { super(); }

  reduce() {
    let currentTodoList = this.state.todoList;
    let newTodoList = currentTodoList.addTodoFromText(this.text);
    
    return this.state.withTodoList(newTodoList);
  }
}
```

Note above we also used function `state.withTodoList()` to create a new state with the new todo
list, and then returned this new state from the reducer.

## What if the item already exists?

Let's now modify `AddTodoAction` to check if the new todo item being added
already exists in the list. If it does, we want to abort adding the new todo item,
and then show an **error message** to the user.

This can be accomplished by simply throwing a `UserException` with the error message.
See below:

```tsx
class AddTodoAction extends Action {
  constructor(readonly text: string) { super(); }

  reduce() {
  
    let currentTodoList = this.state.todoList;
  
    // Check if the item already exists
    if (currentTodoList.ifExists(this.text)) {
      throw new UserException(
        `The item "${this.text}" already exists.`, {
          errorText: `Type something else other than "${this.text}"`
        });
    }

    let newTodoList = currentTodoList.addTodoFromText(this.text);
    return this.state.withTodoList(newTodoList);
  }
}
```

In the code above, we use the `ifExists` function defined in the `TodoList` class to check if the
new todo item already exists in the list. When it does, we throw a `UserException` with an error
message.

Throwing a `UserException` from inside actions is ok. The app will not crash!
Kiss will catch the exception and handle it properly:

* The action will abort. The reducer will not return a new state, and the store state will not
  be updated
* A dialog will pop up with the error message, automatically
* Components can later check an error occurred by writing `useIsFailed(AddTodoAction)`
* Components can later get a reference to the error itself by doing `useExceptionFor(AddTodoAction)`

In the next page, we will see how the `TodoInput` component handles this error.

:::tip

All actions, sync or async, can be dispatched with the following functions:

* `dispatch` - Dispatches the action and returns immediately.
* `dispatchAndWait` - Dispatches the action and returns a `Promise` that resolves
  with a "status" that tells us if the action was successful or not.

Here's why the [TodoInput](the-basic-ui#todoinput) component actually uses `dispatchAndWait`
instead of `dispatch`:

```tsx
// Add the item if it's unique. Fails if its text is a duplicate
let status = await store.dispatchAndWait(new AddTodoAction(text));

// Only if the item was added, clear the input
if (status.isCompletedOk) setInputText('');
```

:::

## RemoveAllTodosAction

In the [previous page](the-basic-ui.md#removeallbutton)
we discussed the `RemoveAllButton` component, which dispatches a `RemoveAllTodosAction`
when clicked. This action needs to return the state with an empty todo list.

The [State](creating-the-state#state) class has a `withTodoList()` function that returns the
state with a given todo list, and the [TodoList](creating-the-state#todolist) class
has a static `TodoList.empty` todo list. We'll use both:

```tsx
class RemoveAllTodosAction extends Action {

  reduce() {
    return this.state.withTodoList(TodoList.empty);
  }
}
```

## Note

In Kiss, all actions must extend `KissAction<State>`,
assuming `State` is the type that represents the state of your app.

In the code above, and for the rest of this tutorial,
I'm assuming you have defined your own base action class called simply `Action`
that extends `KissAction<State>`, and then have all your actions
extend this `Action` class instead.

This is how you would define the `Action` class:

```tsx 
import { KissAction } from 'kiss-for-react';
import { State } from 'State';

export abstract class Action extends KissAction<State> {
}
```

The reason to do this is twofold:

* First, you'll avoid writing `extends KissAction<State>` in every action class.
  Now, you'll need to write `extends Action` instead.

* And second, to have a common place to put any common logic that all your actions should have
  access to. More on that later.

## Async Actions

# Asynchronous actions

The `AddTodoAction` previously described is a **[synchronous](./sync-actions)** action.
It adds a new todo item to the list and returns the new state immediately.

Now let's see how we can create **asynchronous** actions,
that can involve network requests, file system operations,
or any other kind of asynchronous operations.

## Add Random Todo

Let's create an action called `AddRandomTodoAction` that will read some text from a server,
and use this text to add a new todo item to the list.

We'll be using an API called "Dummy Json", which is openly available at https://dummyjson.com.

Here is the action:

```tsx title="AddRandomTodoAction.ts"
class AddRandomTodoAction extends Action {

  async reduce() {
    let response = await fetch("https://dummyjson.com/todos/random/1");
    if (!response.ok) throw new UserException("Failed to load.");
    
    let jsonResponse = await response.json();
    let text = jsonResponse[0].todo;
     
    return (state) => state.withTodoList(this.state.todoList.addTodoFromText(text));
  }
} 
``` 

As you can see above, the `reduce` method is now `async`. This allows us to use the `await` keyword
to wait for the server response.

Another difference is that now we are **not** returning the new state directly.
Instead, we are returning a **function** that receives the current state and returns the new state:

```tsx
return (state) => state.withTodoList(this.state.todoList.addTodoFromText(text)); 
``` 

This is necessary when the action is asynchronous, because of the way Promises work in JavaScript.
When using TypeScript, you don't need to worry about forgetting this:
If you try to return the new state directly from an asynchronous action,
Kiss will show a compile time error.

## Adding a spinner

We'll also add an `AddRandomTodoButton` with label "Add Random Todo" to our _Todo List_ app.
When clicked, this button dispatches the action we've created above.

We want to show a spinner (also called "circular progressor" or "activity indicator")
or a `Loading...` text, while the action is running. We also want to disable the button while
loading, so that the user can't click it multiple times.

We can use the `useIsWaiting` hook, that returns `true` when the action is running,
and `false` when it is not:

```tsx
function AddRandomTodoButton() {

  let isLoading = useIsWaiting(AddRandomTodoAction);
  const store = useStore();

  return (
    <button
        onClick={() => store.dispatch(new AddRandomTodoAction())}
        disabled={isLoading}
        >    
        { isLoading 
          ? 'Loading...' 
          : 'Add Random Todo'
        }
    </button>
  );
};
```

```tsx 
function AddRandomTodoButton() {

  let isLoading = useIsWaiting(AddRandomTodoAction);
  const store = useStore();

  return (
    <TouchableOpacity 
      onPress={() => store.dispatch(new AddRandomTodoAction())}>
        { isLoading 
          ? <ActivityIndicator /> 
          : <Text>Add Random Todo</Text>
        }
    </TouchableOpacity>
  );
};
```

## Combining isWaiting and isFailed

You can combine the `useIsWaiting` and `useIsFailed` hooks
to show both a spinner if the information is loading,
and an error message if the loading fails:

```tsx
function AddRandomTodoButton() {

  let isLoading = useIsWaiting(AddRandomTodoAction);
  let isFailed = useIsFailed(AddRandomTodoAction);
  let errorText = useExceptionFor(AddRandomTodoAction)?.errorText ?? '';
  const store = useStore();

  return (
    <div>
      <button
          onClick={() => store.dispatch(new AddRandomTodoAction())}
          disabled={isLoading}
          { isLoading 
            ? 'Loading...' 
            : 'Add Random Todo'
          }
      </button>
      {isFailed && <div>{errorText}</div>}
    </div>
  );
};
```

```tsx
function AddRandomTodoButton() {

  let isLoading = useIsWaiting(AddRandomTodoAction);
  let isFailed = useIsFailed(AddRandomTodoAction);
  let errorText = useExceptionFor(AddRandomTodoAction)?.errorText ?? '';
  const store = useStore();

  return (
    <View>
      <TouchableOpacity 
        onPress={() => store.dispatch(new AddRandomTodoAction())}>
          { isLoading 
            ? <ActivityIndicator /> 
            : <Text>Add Random Todo</Text>
          }
      </TouchableOpacity>
      {isFailed && <Text>{errorText}</Text>}
    </View>
  );
};
```

<br></br>

## Try it yourself

Click the "Add Random Todo" button below, to add random todo items to the list:

Then, check the code below to see how the `AddRandomTodoAction` and `AddRandomTodoButton` are
implemented.

**[Live Example](https://codesandbox.io/s/mrkz2d)**

## Handling Action Errors

We've seen how the [TodoInput](the-basic-ui#todoinput) component dispatches
an `AddTodoAction` to add a new todo item in the todo list.
We've also seen that the action checks if the new todo
item [already exists](sync-actions#what-if-the-item-already-exists) in the list and throws an error
of type `UserException` if it does:

In the `AddTodoAction`:

```tsx 
// Check if the item already exists
if (currentTodoList.ifExists(this.text)) {
  throw new UserException(
    `The item "${this.text}" already exists.`, {
    errorText: `Type something else other than "${this.text}"`
  });
}
```

As you can see above, we can provide both a **message** and an **error text** in the error:

```tsx
UserException("message", errorText: "errorText")
```

We want to accomplish two things:

* Open a dialog (or a toast) to show the `message` to the user.
* Show the `errorText` below the input, until the user starts typing again.

## Show error messages in a dialog

Kiss automatically opens a dialog to show the `message` of all the user exception errors
thrown by actions. For this to work, however, you must set up the desired dialog,
toast, or other suitable UI element.

This is done by providing the `showUserException` parameter, when you create the store:

```tsx
const store = createStore<State>({
  initialState: State.initialState,
  showUserException: userExceptionDialog, // Here!
});
```

For example, the following is a possible `userExceptionDialog` function that opens a dialog with
the error `message` thrown by the action:

```tsx
import { Button, Dialog, DialogActions, DialogContent } from '@mui/material';
import { createRoot } from "react-dom/client";

// Alternative 1: Using a browser dialog
const userExceptionDialog: ShowUserException =
  (exception, count, next) => {
    let message = exception.title ? `${exception.title} - ${exception.message}` : exception.message;
    window.alert(message);
    next();
  };
  
// Alternative 2: Using the MUI library (mui.com) 
export const userExceptionDialog: ShowUserException = (exception: UserException, _count: number, next: () => void) => {
  const container = document.getElementById('dialog-root');
  if (!container) return;
  const root = createRoot(container!);
  const closeDialog = () => {
    root.unmount();
    next();
  };
  root.render(
    <Dialog open={true} onClose={closeDialog}>
      <DialogContent>
        <p>{exception.title || 'Error'}</p>
        <p>{exception.message}</p>
      </DialogContent>
      <DialogActions>
        <Button onClick={closeDialog}>OK</Button>
      </DialogActions>
    </Dialog>
  );
};
```

```tsx 
import { Alert } from 'react-native';

export const userExceptionDialog: ShowUserException =
  (exception, count, next) => {
    Alert.alert(
      exception.title || exception.message,
      exception.title ? exception.message : '',
      [{text: 'OK', onPress: (_value?: string) => next()}]
    );
  };
```

## Showing error messages in components

The second thing we want to accomplish is to show the `errorText` below the input field
until the user starts typing again.

In the `TodoInput` code, let's add the following 3 hooks:

```tsx 
let isFailed = useIsFailed(AddTodoAction);
let errorText = useExceptionFor(AddTodoAction)?.errorText ?? '';
let clearExceptionFor = useClearExceptionFor();
```

The `isFailed` variable will be `true` when the `AddTodoAction` fails.

And when it fails, the `errorText` variable will contain the `errorText` message of the exception,
which was defined in the `AddTodoAction` action:

```tsx 
throw new UserException(
  `The item "${this.text}" already exists.`, {
    errorText: `Type something else other than "${this.text}"`
  });
```

Finally, the `clearExceptionFor` function will clear the error for the `AddTodoAction` action.
Note the error is already cleared automatically when the action is dispatched again.
We only need to clear it manually if we want to clear the error message without dispatching
the action. In the code below, we'll be clearing the error as soon as the user starts typing
again in the input field.

The `TodoInput` component now looks like this:

```tsx 
function TodoInput() {

  const [inputText, setInputText] = useState<string>('');

  const store = useStore();
  let isFailed = useIsFailed(AddTodoAction);
  let errorText = useExceptionFor(AddTodoAction)?.errorText ?? '';
  let clearExceptionFor = useClearExceptionFor();

  async function processInput(text: string) {
    let status = await store.dispatchAndWait(new AddTodoAction(text))
    if (status.isCompletedOk) setInputText('');
  }

  return (
    <div>
      <TextField className='inputField'
        error={isFailed}
        helperText={isFailed ? errorText : ""}
        value={inputText}
        onChange={(e) => {          
          setInputText(e.target.value);
          clearExceptionFor(AddTodoAction);
        }}
        onKeyDown={(e) => {
          if (e.key === 'Enter') processInput(inputText);
        }}
      />
      
      <Button onClick={() => processInput(inputText)}>Add</Button>
    </div>
  );
};
```

```tsx 
function TodoInput() {

  const [inputText, setInputText] = useState<string>('');
  
  const store = useStore();
  let isFailed = useIsFailed(AddTodoAction);
  let errorText = useExceptionFor(AddTodoAction)?.errorText ?? '';
  let clearExceptionFor = useClearExceptionFor();

  async function processInput(text: string) {
    let status = await store.dispatchAndWait(new AddTodoAction(text));
    if (status.isCompletedOk) setInputText('');
  }

  return (
    <View>    
      <TextInput
        placeholder={'Type here...'}
        value={inputText}          
        onChangeText={(text) => {            
          setInputText(text);
          clearExceptionFor(AddTodoAction);
        }}
        onSubmitEditing={() => processInput(inputText)}
      />

      <TouchableOpacity onPress={() => processInput(inputText)}>
        <Text>Add</Text>
      </TouchableOpacity>
      
      {isFailed && <Text>{errorText}</Text>}
    </View>
  );
};
```

To see it working, just add a todo item with the text `Buy milk`
and then try adding another todo with the same text again.
A dialog will pop up with the following error message:

> The item "Buy milk" already exists

<br></br>

At the same time, an error text will appear below the input field with the `errorText`
that was defined in the user exception thrown by the action reducer:

> Type something else other than "Buy milk"

<br></br>

This is the code used above to show the error text below the input field:

```tsx 
// React Web
helperText={isFailed ? errorText : ""}

// React Native
{isFailed && <Text>{errorText}</Text>}
```

<br></br>

As soon as you start typing in the input field, the error text will disappear.
This is the code used above to clear the error text when the user starts typing again:

```tsx 
// React Web
onChange={(e) => {          
  setInputText(e.target.value);
  clearExceptionFor(AddTodoAction);
}}

// React Native
onChangeText={(text) => {            
  setInputText(text);
  clearExceptionFor(AddTodoAction);
}}
```

## Try it yourself

Type "Buy milk" in the input and press `Enter`.
Do it **twice** and a browser dialog will open with the error message:
`The item "Buy milk" already exists`.

As soon as you close the dialog, you'll also see the error text in red, below the input field:
`Type something else other than "Buy milk"`.

Start typing again in the input field and the error text will disappear.

**[Live Example](https://codesandbox.io/s/wh23g4)**

## How to disable the dialog

To disable showing the dialog for **some** specific errors,
simply add `.noDialog` to them:

```tsx
throw new UserException(`The item "${this.text}" already exists.`, {
  errorText: `Type something else other than "${this.text}"`,
}).noDialog; // Here!
```

Try adding `.noDialog` to the code of the running example above 
and see that the dialog doesn't show up anymore,
but the error message below the input field still works.

## Persisting The State

Finally, let's add a **persistor** to save the state to the local device disk.
This is done during the store creation:

```tsx          
const store = createStore<State>({  
  initialState: State.initialState,
  showUserException: userExceptionDialog,
  persistor: persistor, // Here!
});        
```

You can implement your own persistor, as long as it follows the abstract `Persistor` interface,
provided by Kiss.

Adding a persistor allows the user to reload the page,
or close the browser and reopen it later, without losing the todo list.

## ClassPersistor

Kiss comes out of the box with the `ClassPersistor` that implements the `Persistor`
interface. It serializes ES6 classes out of the box,
and it will persist the whole state of your application.

To use it, you must provide:

* `loadSerialized`: a function that returns the serialized state.
* `saveSerialized`: a function that saves the serialized state.
* `deleteSerialized`: a function that deletes the serialized state.
* `classesToSerialize`: an array of all the _custom_ classes that are part of your state.

In more detail, here's the `ClassPersistor` constructor signature:

```tsx
constructor(

  // Returns the serialized state.
  // It should return a Promise that resolves to the serialized state, 
  // or to null if the state is not yet persisted.
  public loadSerialized: () => Promise<string | null>,
    
  // Saves the given serialized state. 
  // It should return a Promise that resolves when the state is saved.    
  public saveSerialized: (serialized: string) => Promise<void>,
    
  // Deletes the serialized state. 
  // It should return a Promise that resolves when the state is deleted.
  public deleteSerialized: () => Promise<void>,
    
  // List here all the custom classes that are part of your state, directly 
  // or indirectly. Note: You don't need to list native JavaScript classes. 
  public classesToSerialize: Array<ClassOrEnum>
)
```

<br></br>

For our _Todo List_ app, this is the complete code:

```tsx 
let persistor = new ClassPersistor<State>(

  // loadSerialized
  async () => window.localStorage.getItem('state'),
  
  // saveSerialized
  async (serialized: string) => window.localStorage.setItem('state', serialized),
  
  // deleteSerialized
  async () => window.localStorage.clear(),
  
  // classesToSerialize
  [State, TodoList, TodoItem, Filter]
);
```

```tsx
let persistor = new ClassPersistor<State>(

  // loadSerialized
  async () => await AsyncStorage.getItem('state'),
  
  // saveSerialized
  async (serialized) => await AsyncStorage.setItem('state', serialized),
  
  // deleteSerialized
  async () => await AsyncStorage.clear(),
  
  // classesToSerialize
  [State, TodoList, TodoItem, Filter] 
);
```

<br></br>

## Try it yourself

Add some items to the list, mark some of them as completed, and then **reload the page**.
You should see the same items and their completed status.

**[Live Example](https://codesandbox.io/s/sw3g2t)**

## Implementing your own serializer

If instead of using the provided `ClassSerialized` you decide to implement your own 
serializer, the following information may be useful for you.

In JavaScript there are two types of objects:

* Plain (literal) objects - Instances of the `Object` class.
  Sometimes they are called literal objects, when created via the `{}` notation.

* Class (constructor) objects - Instances of classes with own defined constructor,
  properties and methods. Usually you define them via class notation.

If you are loading a JSON from your backend, after you `JSON.parse()` it,
you have a plain JavaScript object, not an instance of a class.

These packages will help you transform plain JavaScript objects into ES6 classes: 

* [esserializer](https://www.npmjs.com/package/esserializer) - This is the package used internally
  by the `ClassSerialized`. ESSerializer is a JavaScript serialization and deserialization utility,
  that helps you serialize classes to JSON, and then deserialize them back to class objects,
  with all properties and methods, recursively. No eval is used, which means it doesn't introduce
  any security risks.

* [class-transformer](https://www.npmjs.com/package/class-transformer) - Allows you to transform
  plain JavaScript objects into some class object and versa, and also serialize and deserialize
  them.

## Testing

How would we go about testing our _Todo List_ app?

It's straightforward: let's dispatch some actions, wait for them to finish,
and then verify if the new state is as expected, or check if some error was thrown.

We have the following actions in our app:

* `AddTodoAction`
* `RemoveAllTodosAction`
* `RemoveCompletedTodosAction`
* `AddRandomTodoAction`
* `ToggleTodoAction`
* `NextFilterAction`

## Basic action testing

Let's start with the `AddTodoAction`.
This action adds a new todo item with the given text to the list.
That is, unless the text already exists in the list, in which case it throws an error.

This is a possible test for this action:

```tsx
test('AddTodoAction', async () => {

  const store = createStore<State>({ initialState: State.initialState });
  
  // Should add a new todo item, with text 'Some text'
  await store.dispatchAndWait(new AddTodoAction('Some text'));  
  expect(store.state.todoList.items[0].text).toBe('Some text');
  
  // Fail to add the same text again
  let status = await store.dispatchAndWait(new AddTodoAction('Some text'));    
  expect(status.originalError).toBeInstanceOf(UserException);          
});
```

Easy, right? We create a new store, dispatch the action, and check the new state.
The `dispatchAndWait` method waits for the action to finish dispatching,
and returns the action `status`, which contains detailed information about the dispatch,
including any errors thrown.

## Setting the initial state for the test

Now, let's test the `RemoveAllTodosAction`. We expect this action to remove all todo items from the
list, which means we must first add some items to the list.

In other words, we must set the initial state of the store as a prerequisite for this test.

There are a few ways to do this. For example, we can first create the store,
and then dispatch actions to change its initial state into the desired one:

```tsx
const store = createStore<State>({ initialState: State.initialState });
store.dispatch(new AddTodoAction('First todo'));
store.dispatch(new AddTodoAction('Second todo'));
```

Alternatively, we could have already created the store with the proper
initial state, in a single step:

```tsx
const store = createStore<State>({
  initialState: 
    State.initialState.withTodoList(
      new TodoList(
        [
        new TodoItem('First todo', false),
        new TodoItem('Second todo', true),
        ]
      )
    )
});
```

Or, we could have created the state separately:

```tsx
let item1 = new TodoItem('First todo', false);
let item2 = new TodoItem('Second todo', true);
let todoList = new TodoList([item1, item2]);
let state = State.initialState.withTodoList(todoList);
 
const store = createStore<State>({ initialState: state });
```

The rest of the test is straightforward. This is the complete code:

```tsx
test('RemoveAllTodosAction', async () => {

  let item1 = new TodoItem('First todo', false);
  let item2 = new TodoItem('Second todo', true);
  let todoList = new TodoList([item1, item2]);
  let state = State.initialState.withTodoList(todoList);
     
  const store = createStore<State>({ initialState: state });
  
  // Should remove all todo items
  await store.dispatchAndWait(new RemoveAllTodosAction());  
  expect(store.state.todoList.items.length).toBe(0);          
});
```

## Testing asynchronous actions

Both `AddTodoAction` and `RemoveAllTodosAction` above are "synchronous",
meaning they don't involve any asynchronous operation. We know this by looking at their reducers,
which are declared with `reduce()`.

The only asynchronous action we have in our app is `AddRandomTodoAction`, which is declared
with `async reduce()` and returns a `Promise`. This action fetches a random todo item
from an external API:

```tsx
class AddRandomTodoAction extends Action {
  async reduce() {
    let response = await fetch("https://dummyjson.com/todos/random/1");
    if (!response.ok) throw new UserException("API failed.");
    let jsonResponse = await response.json();
    let text = jsonResponse[0].todo;
    
    return (state: State) =>
      state.withTodoList(this.state.todoList.addTodoFromText(text));
  }
}
```

Testing an asynchronous action is just as easy, and **not** different from testing a synchronous
one. We still dispatch the action, wait for it to finish, and check the new state.

```tsx 
test('AddRandomTodoAction', async () => {

  const store = createStore<State>({ initialState: State.initialState });
  
  // Should add a new todo item
  await store.dispatchAndWait(new AddRandomTodoAction());  
  expect(store.state.todoList.items.length).toBe(1);              
});
```

The test above is calling the real external API.
This works, but note we can't check the text of the new
todo item, because it's random. We could instead mock or simulate the API call to have it return a
fixed value, and then check if the new todo item has the expected text.

To that end, let's first go back to the `AddRandomTodoAction` code,
and extract the API call into a separate function called `fetchRandomTodo`:

```tsx
class AddRandomTodoAction extends Action {  

  async reduce() {
    let text = await this.fetchRandomTodo();    
    return (state: State) => state.withTodoList(this.state.todoList.addTodoFromText(text));
  }
  
  async fetchRandomTodo() {   
    let response = await fetch("https://dummyjson.com/todos/random/1");
    if (!response.ok) throw new UserException("API failed.");
    let jsonResponse = await response.json();
    return jsonResponse[0].todo;
  }
}
```

We can now mock the `fetchRandomTodo` method in our test:

```tsx
class MockAddRandomTodoAction extends AddRandomTodoAction {
  async fetchRandomTodo() {
    return "Fixed text";
  }
}

test('AddRandomTodoAction', async () => {
  const store = createStore<State>({ initialState: State.initialState });
  
  // Here we use the mock action
  await store.dispatchAndWait(new MockAddRandomTodoAction());    
  
  // Now we can check if the new todo item has the expected text    
  expect(store.state.todoList.items[0].text).toBe("Fixed text");          
});
```

The above code is just an example, and it's not the recommended way to mock functions.
You should feel free to use any mocking features of your test framework,
or any mocking library you prefer.

And remember you can also use the real API calls, turning your tests into easy-to-write
integration tests.

## Try it yourself

Now, try to implement the tests for the remaining synchronous actions:

* `RemoveCompletedTodosAction`
* `AddRandomTodoAction`
* `ToggleTodoAction`
* `NextFilterAction`

## Other Improvements

Let's implement some other improvements to our todo list application:

* Add a checkbox to mark a todo item as completed
* Add a filter to show only the completed items, only the uncompleted items, or all items
* Add a button to remove all completed items
* Disable the _Remove All_ button when there are no items to remove
* Disable the _Remove Completed_ button when there are no completed items

## Completing and filtering a todo item

The [TodoItem](creating-the-state#todoitem) class already has a boolean flag called `completed`
that indicates if the todo item is completed or not.

We will:

1. Create the `Filter` enum:

    ```tsx
    export enum Filter {
      showAll = 'Showing ALL',
      showCompleted = 'Showing COMPLETED',
      showActive = 'Showing ACTIVE',
    }
    ```

2. Add the filter state to the `State` class:

   ```tsx
   export class State {
     todoList: TodoList;
     readonly filter: Filter;
   
   constructor({todoList, filter}: { todoList: TodoList, filter: Filter }) {
       this.todoList = todoList;
       this.filter = filter;
     }  
   
     withTodoList(todoList: TodoList): State {
       return new State({ todoList: todoList, filter: this.filter });
     }
     
     withFilter(filter: Filter): State {    
       return new State({todoList: this.todoList, filter: filter});
     }
   
     static initialState: State = new State({ 
       todoList: TodoList.empty, 
       filter: Filter.showAll 
     });
   }
   ```

3. Add a checkbox to the `TodoItemComponent` that allows the user to mark the todo item as
   completed. The checkbox dispatches the `ToggleTodoAction` action.

4. Modify the `TodoItemComponent` so that it only shows the item if it matches the filter.

5. Add the `RemoveCompletedButton` component that dispatches the `RemoveCompletedTodosAction`. The
   button is disabled when there are no completed items.

6. Modify the `RemoveAllButton` component so that it is disabled when there are no items to remove.

7. Add the `FilterButton` component that dispatches the `NextFilterAction` action. This button text is
   based on the current filter: "Showing all", "Showing completed", or "Showing active".

## Try it yourself

Click the "Add Random Todo" a few times, to add some items to the list. Then, mark some of
them as completed, and click the "Showing all" button to see how the filter works.

Read the code below and try to understand how it works.

**[Live Example](https://codesandbox.io/s/qs35kc)**

## Full Code

If you're interested in seeing the full code of _Todo List_ applications that are even
more complete than the one we've built in this tutorial, you can check out these examples
in [Kiss's GitHub repository](https://github.com/marcglasberg/kiss-for-react):

* [Todo List app for **React Web**](https://github.com/marcglasberg/kiss-for-react/tree/main/examples/todo-app-example)
* [Todo List app for **React Native**](https://github.com/marcglasberg/kiss-for-react/tree/main/examples/TodoAppReactNative)

## Tutorial code

As a reference, the following is the full code of the web app we built in this tutorial,
fully commented, in files `index.tsx`, `App.tsx`, `styles.css` and `State.ts`.

Note all components and actions are in `App.tsx`,
and all state types are in `State.ts`,
just to make it easier for you to read the code.
In a real app they would be split it into multiple files.

```tsx title="index.tsx"
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

const rootElement = document.getElementById("root")!;
const root = ReactDOM.createRoot(rootElement);

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```

```tsx title="App.tsx"
import "./styles.css";
import React from "react";
import { useEffect, useState } from "react";
import { Store, StoreProvider, KissAction } from "kiss-for-react";
import { ShowUserException, ClassPersistor } from "kiss-for-react";
import { useSelect, useStore, useIsWaiting } from "kiss-for-react";
import { useIsFailed, useExceptionFor } from "kiss-for-react";
import { useClearExceptionFor, UserException } from "kiss-for-react";
import { State, TodoList, TodoItem, Filter } from "./State";

// Allows the user to reload the page without losing the todo list.
let persistor = new ClassPersistor<State>(
  async () => window.localStorage.getItem("state"),
  async (serialized: string) => window.localStorage.setItem("state", serialized),
  async () => window.localStorage.clear(),
  [State, TodoList, TodoItem, Filter]
);

// Sets up a dialog to show user exceptions thrown by actions.
let userExceptionDialog: ShowUserException = (exception, count, next) => {
  
  let message = exception.title
    ? `${exception.title} - ${exception.message}`
    : exception.message;
    
  // Opens a browser dialog with the exception message.  
  window.alert(message);
  
  // Goes to the next error in the queu, if any.
  next();
};

// We declare the store with its initial state etc.
const store = createStore<State>({
  initialState: State.initialState,
  showUserException: userExceptionDialog,
  persistor: persistor,
});

// Base `Action` class, to simplify declaring actions.
abstract class Action extends KissAction<State> {}

// We wrap the component tree with a `StoreProvider`.
export default function App() {
  return (
    <StoreProvider store={store}>
      <AppContent />
    </StoreProvider>
  );
}

// The app is composed of: 
// - A title
// - An input to add items
// - the list of todo items
// - Buttons to remove all and remove completed items
// - A button to add a random item
// - A filter button to show all, active, or completed items
function AppContent() {
  return (
    <div className="app">
      <h1>Todo List</h1> 
      <TodoInput />
      <ListOfTodos />
      <RemoveAllButton />
      <RemoveCompletedButton />
      <AddRandomTodoButton />
      <FilterButton />
    </div>
  );
}

// Input with an `Add` button.
// When the button is clicked, will dispatch an `AddTodoAction`.
function TodoInput() {
  const [inputText, setInputText] = useState<string>("");
  
  // Hook to access the store.
  const store = useStore();
  
  // Hook to check if the action failed.
  const isFailed = useIsFailed(AddTodoAction);
  
  // Hook to get the error text, in case the action failed.
  const errorText = useExceptionFor(AddTodoAction)?.errorText ?? "";
  
  // Hook to clear the error message, when the user types something new.
  const clearExceptionFor = useClearExceptionFor();
                    
  async function processInput(text: string) {  
    // Add a todo item with the given text.
    const status = await store.dispatchAndWait(new AddTodoAction(text));
    
    // If the action was successful, clear the input text.
    if (status.isCompletedOk) setInputText("");
  }

  // An input for the user to type, an "Add" button, 
  // and an error message in case the action failed. 
  return (
    <div>
      <input
        className={`todoInput ${isFailed ? "inputError" : ""}`}
        maxLength={50}
        placeholder="Type here..."
        value={inputText}
        onChange={(e) => {
          setInputText(e.target.value);
          clearExceptionFor(AddTodoAction);
        }}
        onKeyDown={(e) => {
          if (e.key === "Enter") processInput(inputText);
        }}
      />

      <button onClick={() => processInput(inputText)}>Add</button>
      <div className="errorText">{isFailed && errorText}</div>
    </div>
  );
}

// Vertical list of the todo items in the list.
function ListOfTodos() {

  // Hook to get the list of all todo items from the state.
  const todoItems = useSelect((state: State) => state.todoList.items);

  return (
    <div className="listOfTodos">
      {todoItems.map((item, index) => (
        <TodoItemComponent key={index} item={item} />
      ))}
    </div>
  );
}

// A single todo item in the list.
// A checkbox allows the user to mark the item as completed.
function TodoItemComponent({ item }: { item: TodoItem }) {

  const store = useStore();
  
  // Hook to read the current filter from the state.
  const filter = useSelect((state: State) => state.filter);

  // The item is shown if it's completed or not, according to the filter.
  return item.ifShows(filter) ? (
    <label>
      <input
        type="checkbox"
        checked={item.completed}
        onChange={() => store.dispatch(new ToggleTodoAction(item))}
      />
      <div>{item.text}</div>
    </label>
  ) : (
    <span />
  );
}

// Button removes all items, using `RemoveAllTodosAction`.
function RemoveAllButton() {
  
  // Hook to access the store.
  const store = useStore();
  
  // The button is disabled when there are no items.
  const isDisabled = useSelect((state: State) => state.todoList.isEmpty());

  return (
    <button
      onClick={() => store.dispatch(new RemoveAllTodosAction())}
      disabled={isDisabled}
    >
      Remove All
    </button>
  );
}

// Button removes all completed items, using `RemoveCompletedTodosAction`.
function RemoveCompletedButton() {

  // Hook to access the store.
  const store = useStore();
  
  // The button is disabled when there are no completed items.
  const isDisabled = useSelect(
    (state: State) => state.todoList.countCompleted() === 0
  );

  return (
    <button
      onClick={() => store.dispatch(new RemoveCompletedTodosAction())}
      disabled={isDisabled}
    >
      Remove Completed
    </button>
  );
}

// Button to change the filter, using `NextFilterAction`.
// Filter can be: showAll, showActive, or showCompleted.
function FilterButton() {

  // Hook to access the store.
  const store = useStore();
  
  // Hook to get the current filter from the state.
  const filter = useSelect((state: State) => state.filter);

  return (
    <button onClick={() => { store.dispatch(new NextFilterAction()); }}>
      {filter}
    </button>
  );
}

// Action to add a todo item with the given text.
// If the item already exists, throws an error.
class AddTodoAction extends Action {
  constructor(readonly text: string) { super(); }

  reduce() {
    // Gets the current todo list.
    let currentTodoList = this.state.todoList;

    // If the item being added already exists, throw an error.
    if (currentTodoList.ifExists(this.text)) {
      throw new UserException(`The item "${this.text}" already exists.`, {
        errorText: `Type something else other than "${this.text}"`,
      });
    }

    // Add the item to the list.
    let newTodoList = currentTodoList.addTodoFromText(this.text);
    
    // Return the new state with the updated list.
    return this.state.withTodoList(newTodoList);
  }
}

// Action to remove all todo items from the list.
export class RemoveAllTodosAction extends Action {
  reduce() {
    // Return the new state with an empty todo list.
    return this.state.withTodoList(TodoList.empty);
  }
}

export class RemoveCompletedTodosAction extends Action {
  reduce() {
    // Return the new state with the todo list, but without the 
    // completed items. The filter is also reset to show all items.
    return this.state      
      .withTodoList(this.state.todoList.removeCompleted())
      .withFilter(Filter.showAll);
  }
}

// Action to add a random todo item, read from an API.
class AddRandomTodoAction extends Action {
  async reduce() {
  
    // Calls an API to get a random todo item.
    let response = await fetch("https://dummyjson.com/todos/random/1");
    
    // If the API call failed, throw an error.
    if (!response.ok) throw new UserException("API failed.");

    // Get the text of the todo item from the response json.
    let jsonResponse = await response.json();
    let text = jsonResponse[0].todo;

    // Return the new state with the updated todo list.
    return (state: State) =>
      state.withTodoList(this.state.todoList.addTodoFromText(text));
  }
}

// Button to add a random todo item, using `AddRandomTodoAction`.
function AddRandomTodoButton() {
  
  // Hook to access the store.
  const store = useStore();
  
  // Hook to check if the given action is currently loading.
  const isLoading = useIsWaiting(AddRandomTodoAction);

  return (
    <button
      onClick={() => store.dispatch(new AddRandomTodoAction())}
      disabled={isLoading}
    >
      {isLoading ? "Loading..." : "Add Random Todo"}
    </button>
  );
}

// Action to toggle the completed status of a todo item.
class ToggleTodoAction extends Action {
  constructor(readonly item: TodoItem) { super(); }

  reduce() {
    let newTodoList = this.state.todoList.toggleTodo(this.item);
    return this.state.withTodoList(newTodoList);
  }
}

// Action to change the filter to the next one.
export class NextFilterAction extends Action {
  reduce() {
    let newFilter = this.state.filter;

    switch (newFilter) {
    
      // If the current filter is showActive, the next is showCompleted.
      case Filter.showActive:
        newFilter = Filter.showCompleted;
        break;
        
      // If the current filter is showCompleted, the next is showAll.
      case Filter.showCompleted:
        newFilter = Filter.showAll;
        break;
        
      // If the current filter is showAll, the next is showActive.
      case Filter.showAll:
        newFilter = Filter.showActive;
        break;
        
      // Make sure we handled all cases.
      default:
        throw new Error("Unknown filter: " + newFilter);
    }

    return this.state.withFilter(newFilter);
  }
}
```

```css title="styles.css"
.app {
  font-family: sans-serif;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 100vh;
  background-color: #f0f0f0;
}

h1 {
  margin-bottom: 0;
}

button {
  height: 35px;
  min-width: 60px;
  background-color: rgb(72, 149, 208);
  color: white;
  border: 0;
  cursor: pointer;
  padding: 10px 20px;
  margin: 18px 0px 8px 0px;
}

button:disabled {
  opacity: 0.5;
}

input {
  height: 30px;
  padding-left: 8px;
  text-transform: capitalize;
  margin-right: 4px;
  margin-bottom: 5px;
}

input[type="checkbox"] {
  vertical-align: middle;
  margin-right: 8px;
}

label {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
}

.listOfTodos {
  text-align: left;
  background-color: rgb(230, 230, 230);
  padding: 12px 20px 20px 20px;
  max-width: 200px;
  width: 100%;
  box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.1);
}

.listOfTodos > div {
  margin-bottom: 4x; 
}

.removeAllButton {
  background-color: blue;
  color: white;
  height: 40px;
  margin-top: 20px; 
}

.inputError {
  border: 1px solid red;
  background-color: #ffe6e6;
}

.errorText {
  color: red;
  font-size: 12px;
  margin-bottom: 14px;
  height: 12px;
}
```

```tsx title="State.ts"
// A single todo item.
export class TodoItem {
  constructor(public text: string, public completed: boolean = false) {}

  toggleCompleted() {
    return new TodoItem(this.text, !this.completed);
  }

  ifShows(filter: Filter) {
    return (
      filter === Filter.showAll ||
      (filter === Filter.showCompleted && this.completed) ||
      (filter === Filter.showActive && !this.completed)
    );
  }
}

// The list of all todo items.
export class TodoList {
  constructor(public readonly items: TodoItem[] = []) {}

  addTodoFromText(text: string): TodoList {
    const trimmedText = text.trim();
    const capitalizedText =
      trimmedText.charAt(0).toUpperCase() + trimmedText.slice(1);
    return this.addTodo(new TodoItem(capitalizedText));
  }

  addTodo(newItem: TodoItem): TodoList {
    if (newItem.text === "" || this.ifExists(newItem.text)) return this;
    else return new TodoList([newItem, ...this.items]);
  }

  ifExists(text: string): boolean {
    return this.items.some(
      (todo) => todo.text.toLowerCase() === text.toLowerCase()
    );
  }

  removeTodo(item: TodoItem): TodoList {
    return new TodoList(
      this.items.filter((itemInList) => itemInList !== item)
    );
  }

  removeCompleted(): TodoList {
    return new TodoList(
      this.items.filter((itemInList) => !itemInList.completed)
    );
  }

  toggleTodo(item: TodoItem): TodoList {
    const newTodos = this.items.map((itemInList) =>
      itemInList === item ? item.toggleCompleted() : itemInList
    );
    return new TodoList(newTodos);
  }

  isEmpty() {
    return this.items.length === 0;
  }

  countCompleted(): number {
    return this.items.filter((item) => item.completed).length;
  }

  *[Symbol.iterator]() {
    for (let i = 0; i < this.items.length; i++) {
      yield this.items[i];
    }
  }

  toString() {
    return `TodoList{${this.items.join(",")}}`;
  }

  static empty: TodoList = new TodoList();
}

export enum Filter {
  showAll = "Showing ALL",
  showCompleted = "Showing COMPLETED",
  showActive = "Showing ACTIVE",
}

// The app state
export class State {
  todoList: TodoList;
  readonly filter: Filter;

  constructor({
    todoList,
    filter,
  }: {
    todoList: TodoList;
    filter: Filter;
  }) {
    this.todoList = todoList;
    this.filter = filter;
  }

  withTodoList(todoList: TodoList): State {
    return new State({ todoList: todoList, filter: this.filter });
  }

  withFilter(filter: Filter): State {
    return new State({ todoList: this.todoList, filter: filter });
  }

  static initialState: State = new State({
    todoList: TodoList.empty,
    filter: Filter.showAll,
  });
}
```

## Conclusion

This concludes the tutorial on how to create a _Todo List_ application with Kiss for React.
I hope you agree with me that it was quite easy!

I also hope you enjoyed the tutorial and found it useful. 
If you have any questions or feedback regarding **this Tutorial**, please feel 
free to [open an issue here](https://github.com/marcglasberg/kissforreact.org/issues).

Or, if you have any questions or feedback regarding Kiss 
itself, [open an issue here](https://github.com/marcglasberg/kiss-for-react/issues).

<hr></hr>

There's a lot more to learn about Kiss, so feel free to explore the rest of the
documentation.

---

# Basics

## Actions And Reducers

In Kiss, an **action** is any class you create that extends `KissAction<State>`.

The `KissAction` is a built-in class provided by Kiss,
and `State` is the type you defined for your application [state](./store-and-state).

For example, this is how you can declare an `Increment` action,
that could be used in a [counter application](./counter-app-examples):

```tsx
import { KissAction } from "kiss-for-react";
import { State } from 'State';

class Increment extends KissAction<State> { }
```

## The reducer

All your actions must implement a function called `reduce()`.
Your IDE will show a compile-time error if you forget to implement it.

```tsx
class Increment extends KissAction<State> {

  reduce() { 
    // ... 
  }
}
```

The `reduce()` function is called a **reducer**.

We'll soon see that when actions are "[dispatched](./dispatching-actions)",
its reducer will be called to calculate state changes in your app.

To achieve this, the reducer has direct access to the current application state
through `this.state`, and then it must return a new state. For example:

```tsx
class Increment extends KissAction<State> {

  reduce() { 
    // The reducer has access to the current state
    return new State(this.state.counter + 1); // Returns a new state 
  }
}
```

:::tip

In the code that dispatches an action, you can use your IDE to click the action name and go to where
the action is defined. There, you'll find the reducer for that action, which explains what happens
when the action is dispatched.

In other words, the action and its reducer are part of the same data structure,
keeping your code organized.

:::

## Base action

Having to write `extends KissAction<State>` in every action definition can be cumbersome.

In all the code I show in this documentation, you'll see I usually write `extend Action`
instead of `extend KissAction<State>`.

This is because I'm assuming you have previously defined your own abstract base action class
called simply `Action`, that itself extends `KissAction<State>`. Then, you may have all your
actions extend this `Action` class instead.

This is how you can define the `Action` class in your own code:

```tsx
import { KissAction } from 'kiss-for-react';
import { State } from 'State';

export abstract class Action extends KissAction<State> { }
```

And then:

```tsx
import { Action } from './Action';

class Increment extends Action { 
  // ... 
}
```

Later, we'll see that the base action is also a good place to
put [common logic](../advanced-actions/base-action-with-common-logic).

## Actions can have parameters

The above `Increment` action is simple and doesn't take any parameters.

But actions can take any number of parameters, just like functions.
Consider the following `Add` action:

```tsx
class Add extends Action {
  constructor(readonly value: number) { super(); }
    
  reduce() {
    return this.state.add(this.value);
  }
}
```

In the above example, the `Add` action takes a `value` parameter in its constructor.
When you dispatch the `Add` action, you pass the value as a parameter:

```tsx
dispatch(new Add(5));
```

Note the reducer has direct access to the `value` parameter through `this.value`.

## Actions can do asynchronous work

The simplest type of action is _synchronous_, meaning it doesn't involve any asynchronous operation.
We can know an action is sync by looking at its reducer, which is declared with `reduce()`.

However, action can download information from the internet, or do any other async work.
To make an action async, declared it with `async reduce()` and then returns a `Promise`.

Also, instead of returning the new state directly, you should return a **function** that
will change the state.

For example, consider the following `AddRandomText` action,
that fetches a random text from the internet and adds it to the state:

```tsx
class AddRandomText extends Action {

  async reduce() {
    let response = await fetch("https://dummyjson.com/todos/random/1");        
    let jsonResponse = await response.json();
    let text = jsonResponse[0].todo;
     
    return (state) => state.copy({text: text}));
  }
} 
```

## Actions can throw errors

If something bad happens, your action can simply **throw an error**.
In this case, the state will not change.

Let's modify the previous `AddRandomText` action to throw an error if the fetch fails:

```tsx
import { UserException } from "kiss-for-react";

class AddRandomText extends Action {

  async reduce() {
    let response = await fetch("https://dummyjson.com/todos/random/1");
    if (!response.ok) throw new UserException("Failed to load.");
    
    let jsonResponse = await response.json();
    let text = jsonResponse[0].todo;
     
    return (state) => state.copy({text: text}));
  }
} 
```

Notes:

* Any errors thrown by actions are caught globally and can be handled in a central place.
  More on that, later.

* Actions can throw any type of errors. However, if they throw a `UserException`
  (provided by Kiss), a dialog or other UI will open automatically,
  showing the error message to the user.

<hr></hr>

Next, let's see how and why you can have actions that don't modify the state.

## Changing State Is Optional

For both sync and async reducers, returning a new state is optional. If you don't plan on changing
the state, simply return `null`. This is the same as returning the state unchanged:

```ts
class GetAmount extends Action {
  
  reduce() async {    
    let amount = await getAmount();
    
    if (amount == 0) 
      return null;
    else 
      return (state) => state.copy({counter: state.counter + amount}));
  }
}
```

This also works:

```ts
class GetAmount extends Action {
  
  reduce() async {    
    let amount = await getAmount();
    
    return (state) => 
      (amount == 0) 
        ? null
        : state.copy({counter: state.counter + amount}));        
  }
}
```
   
<br></br>

Returning `null` is also useful when your action should not change the state, 
but simply start other async processes, or [dispatch](./dispatching-actions) other actions. For example:

```ts
class Initialize extends Action {
  
  reduce() {
    this.dispatch(new ReadDatabaseAction());        
    this.dispatch(new StartTimersAction());          
    this.dispatch(new TurnOnListenersAction());
              
    return null;          
  }
}
```

Note the `reduce()` method has direct access to `dispatch`. 

<hr></hr>

Next, let's see how to dispatch actions.

## Components Use The State

Once you [wrapped your app with a `StoreProvider`](./provide-the-store),
you can access the store's state from any component, by using one of three special hooks 
provided by Kiss.

## useAllState

The `useAllState` hook lets you access the state from any component.
The component will then rebuild whenever the state changes:

```tsx
function MyComponent() { 
  const state = useAllState();   
  
  return <div>{state.name} has {state.age} years old</div>;    
};
```

The problem with `useAllState` is that it rebuilds the component 
every time anything in the state changes,
even if the component doesn't use the part of the state that changed.

## useSelect

The `useSelect` hook (which can also be written as `useSelector`)
selects only the part of the state that your component needs.

It will rebuild only when that part changes:

```tsx
function MyComponent() { 
  const name = useSelect((state) => state.name);   
  const age = useSelect((state) => state.age);
     
  return <div>{name} has {age} years old</div>;    
};
```

In other words, it is more efficient, but also a little more verbose to use.

## useObject

Finally, the `useObject` hook is another alternative that only rebuilds when needed:

```tsx
function MyComponent() {
 
  const state = useObject((state) => {
    name: state.name, 
    age: state.age
  });
       
  return <div>{state.name} has {state.age} years old</div>;    
};
```

The component will now rebuild only when the internal properties of the selected object change.
In other words, when at least one of `name` or `age` changes.

## Try it out

**[Live Example](https://codesandbox.io/s/q2gjmw)**

<hr></hr>

Next, let's see how to define actions and reducers,
that allows us to change the store state.

## Counter App Examples

This concludes the [basics](../category/basics/) of **Kiss**.

But, before we move on to more [advanced](../category/advanced-actions/) concepts,
let's review the basics by creating a few simple "counter application" examples.

The examples below are editable and runnable.
You can change the code and see the results in real-time.

## State as a number

Let's start as simple as possible.
In this first example, the state is just a number of type `number`,
and the initial state is `0`:

```tsx
const store = createStore<number>({
  initialState: 0,
});
```

We'll create an `Increment` action that increments that state by 1, every time it's dispatched.
Since the state itself is just a number,
the action reducer must return `this.state + 1` to increment it.

```tsx
class Increment extends KissAction<number> {
  reduce() {
    return this.state + 1; 
  }
}
```

To create a component that shows the counter in the screen,
we use the `useAllState` hook. This hooks returns the whole state,
which here is the counter value itself: `const counter = useAllState()`.

We also need a second hook called `useStore`, which gives us a reference to the store
with `const store = useStore()`. This is necessary because when the user clicks
a button we want to dispatch the `Increment` action with `store.dispatch(...)`.

Please read the code below and see if you understand everything.

**[Live Example](https://codesandbox.io/s/vprx7v)**

<br></br>         
<br></br>

Try modifying the above code to add a second button named "Decrement" that decrements the counter
by 1 by dispatching an action called `Decrement`.

## State as a plain JavaScript object

In this second example, the state is a plain JavaScript object.
If we use TypeScript, we can define its type like this:

```tsx
type State = {
  counter: number;
};
```

The initial state is an object with counter zero:

```tsx
const store = createStore<State>({
  initialState: {
    counter: 0,
  },
});
```

The `Increment` action increments the state by 1.
The state is now an object of type `State`, which means the counter is `state.counter`.
The action reducer returns a new object, incrementing the counter by one:
`{ counter: this.state.counter + 1 }`.

```tsx
class Increment extends KissAction<State> {
  reduce() {
    return {
      counter: this.state.counter + 1,
    }; 
  }
}
```

To show the counter in the screen we could still use the `useAllState` hook,
which returns the whole state, and then just get the counter value:

```tsx
const state = useAllState();
const counter = state.counter;
```

However, this would mean that every time the state changed, the component would re-render.
That's ok, since in this simple example the state is just the counter anyway, but we can do better.

If we use the `useSelect` hook to "select" just the counter, the component will only re-render
when the counter changes, even when later we add more information to the state.

```tsx
const counter = useSelect((state) => state.counter);
```

In other words, this is an optimization which will prevent unnecessary re-renders when the parts
of the state that change are not the ones we are interested in, in this particular component.

Just as before, we'll also use the `useStore` hook to get a reference to the store and dispatch
the action.

Please read the code below, see if you understand everything,
and compare it with the previous example.

**[Live Example](https://codesandbox.io/s/j57yz5)**

## State as a class

In this third example, the state is of type `State`, which is a **class** we'll create.
It could contain all sorts of information, but in this case, it's just a number counter:

```tsx
class State {
  constructor(public readonly counter: number = 0) {}
}
```

The initial state is an instance of this class: `new State(0)`:

```tsx
const store = createStore<State>({
  initialState: new State(0),
});
```

The `Increment` action increments the state by 1.
The state is now an instance of `State`, which means the counter is `state.counter`.
The action reducer returns a new instance of the class, incrementing the counter by one:
`new State(this.state.counter + 1)`.

```tsx
class Increment extends KissAction<State> {
  reduce() {
    return new State(this.state.counter + 1); 
  }
}
```

We'll use the `useSelect` hook to "select" just the counter, so that the component will only
re-render when the counter changes, even when later we add more information to the state.

```tsx
const counter = useSelect((state) => state.counter);
```

In other words, this is an optimization which will prevent unnecessary re-renders when the parts
of the state that change are not the ones we are interested in, in this particular component.

Just as before, we'll also use the `useStore` hook to get a reference to the store and dispatch
the action.

Please read the code below, see if you understand everything,
and compare it with the previous examples.

**[Live Example](https://codesandbox.io/s/ysfgmk)**

## State modifies itself

If you look at the `Increment` action, you'll see it reads the counter
from the current state, and uses it to create a new, modified state:

```tsx
class Increment extends KissAction<State> {
  reduce() {
    return new State(this.state.counter + 1); 
  }
}
```

While this works, it's breaking the **encapsulation** of the `State` class.
In other words, the knowledge of how to modify the state is outside the state itself.

We can fix this by adding a class function (or more precisely, a _method_) to the `State` class.
This function is called `increment`, and it returns a new state with an incremented counter.

```tsx
class State {
  constructor(public readonly counter: number = 0) {}
  
  increment() { 
    return new State(this.counter + 1); 
  }  
}
```

Now, the `Increment` action may simply call this function:

```tsx
class Increment extends KissAction<State> {
  reduce() {
    return this.state.increment();
  }
}
```

This is a better design, because:

* The `State` class now encapsulates all the knowledge of how to modify itself.
  You may think this is only a small improvement, and it is, but it will make a big difference
  in a real app, when the state becomes complex.

* Adding such functions make it trivial to modify the state and keep it immutable,
  without you ever needing external libraries like [Immer](https://www.npmjs.com/package/immer).

* Finally, it makes the code much easier to test, as you can test the `State` class in isolation,
  without needing to create actions and reducers.

To sum up:

:::tip

In the action, avoid directly accessing parts of the current state to create the new state.
Instead, add functions to the state class that return a new instance with the updated
state, and call these functions from the action.

:::

<br></br>

Check the following code.
It includes state functions to increment and decrement the state, `Increment` and `Decrement`
actions, and respective buttons to dispatch them.

**[Live Example](https://codesandbox.io/s/65vkrq)**

## Functions calling functions

In the above code, the `State` class above has two functions, `increment` and `decrement`:

```tsx
class State {
  constructor(public readonly counter: number = 0) {}
  
  increment() { 
    return new State(this.counter + 1); 
  }
  
  decrement() { 
    return new State(this.counter - 1); 
  }   
}
```

Since functions can call other functions, we can create a parameterized `add` function,
and then modify `increment` and `decrement` to use it:

```tsx
class State {
  constructor(public readonly counter: number = 0) {}

  add(value: number) {
    return new State(this.counter + value);
  }

  increment() { return this.add(1); }
  decrement() { return this.add(-1); }
}
```

Creating simple functions, and then composing them to create more complex, specialized functions,
is a good idea that will simplify your code.

We can also create parameterized **actions**.
For example, we can create an `Add` action that receives a number and calls the `add` function:

```tsx
class Add extends KissAction<State> {
  constructor(readonly value: number) { super(); }
    
  reduce() {
    return this.state.add(this.value);
  }
}
```

In the `Increment` and `Decrement` buttons, we can now dispatch the `Add` action with `1` and `-1`:

```tsx
<Button onClick={() => store.dispatch(new Add(1))}>Increment</Button>
<Button onClick={() => store.dispatch(new Add(-1))}>Decrement</Button>
```

## Defining a Base action

A real app may have dozens or hundreds of actions.
Since all of them must extend `KissAction<State>`, let's create a base class for them,
called `Action`:

```tsx
abstract class Action extends KissAction<State> {}
```

Now, all actions can extend `Action` instead of `KissAction<State>`. For example:

```tsx
class Add extends Action {
  constructor(readonly value: number) { super(); }
    
  reduce() {
    return this.state.add(this.value);
  }
}
```

**[Live Example](https://codesandbox.io/s/vszmfw)**

## Testing state and actions

I suggest you create tests for all your **state** classes.

For example, this is how you could test the above `State` class with [Jest](https://jestjs.io/):

```tsx
import { State } from './path-to-your-state-file'; 

describe('State', () => {

  it('should initialize with a default counter of 0', () => {    
    expect(new State().counter).toBe(0);
  });

  it('should initialize with a given counter value', () => {    
    expect(new State(5).counter).toBe(5);
  });

  it('should increment the counter by 1', () => {      
    expect(new State().increment().counter).toBe(1);
  });

  it('should decrement the counter by 1', () => {       
    expect(new State(5).decrement().counter).toBe(4);
  });

  it('should add a given value to the counter', () => {    
    expect(new State(5).add(3).counter).toBe(8);
  });
});
```

<br></br>

You can also create tests for your **actions**.

However, if your actions mostly call functions in your state classes,
and you already tested those functions as shown above, don't test all the variations again.

Just test enough to make sure the actions are calling the right functions with the right parameters.
For example, this is how I would test the `Add` action, just to make sure it's wired to the `add`
function.

```tsx
import { Store } from 'path-to-your-store-file';
import { State } from 'path-to-your-state-file';
import { Add } from 'path-to-your-action-file';

describe('Add action', () => {
  let store;

  beforeEach(() => {
    store = createStore<State>({ initialState: new State(3) });
  });

  it('should increment the counter by the given value', () => {
    store.dispatch(new Add(5));
    expect(store.state.counter).toBe(8);
  });
});
```

## Asynchronous counter

As one last example, let's create an asynchronous counter.

When the user clicks a button, we'll wait for **1 second** before incrementing the counter.

The async process in this case is simply waiting for 1 second, but note it could be anything
that takes time to finish, like fetching data from a server.

Importantly, while the async process is running the button will be disabled,
so that the user must wait to click the button again.

This is the original, synchronous `Increment` action:

```tsx
class Increment extends Action {
  reduce() {  
    return this.state.add(1);
  }
}
```

To make it asynchronous, we need to:

* Mark the `reduce` function as `async`.
* Add an `await new Promise(...)` that waits for 1 second.
* Instead of returning a new state, return a **function** that returns a new state.

This is the result:

```tsx
class Increment extends Action {
  async reduce() {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return (state) => this.state.add(1);
  }
}
```

<br></br>

We also want to disable the button while the async process is running.
In the component, we can use the `useIsWaiting` hook
to get a boolean that tells us if we're currently waiting for a specific action to finish or not.

In our case, we want to wait until the `Increment` action finishes:

```tsx
const isWaiting = useIsWaiting(Increment);
```

This is the original button:

```tsx
<button onClick={() => store.dispatch(new Increment())}>
  Increment
</button>    
```

All we need to do is set the button's `disabled` property:

```tsx
<button 
    disabled={isWaiting} 
    onClick={() => store.dispatch(new Increment())}>
  Increment
</button>    
```

Try pressing the "Increment" button and see that it disables for 1 second before incrementing the
counter:

**[Live Example](https://codesandbox.io/s/gjh3dj)**

<br></br>
<br></br>

To adapt our tests for the `Increment` action being asynchronous,
we need to apply a small change to them. This doesn't work anymore:

```tsx
it('should increment the counter by one', () => {
  let store = createStore<State>({ initialState: new State(3) });
  
  store.dispatch(new Increment()); // Here! 
  expect(store.state.counter).toBe(4);
});
```

If we dispatch an asynchronous action with function `dispatch`, as shown above,
this function it will return immediately,
and the test will check the state before the action finishes.

Instead, we should use `dispatchAndWait`, which returns a `Promise` that
resolves when the action finishes.
This means we can use `await` to wait for the action to finish, and then check the state:

```tsx
it('should increment the counter by one', async () => {
  let store = createStore<State>({ initialState: new State(3) });
  
  await store.dispatchAndWait(new Increment()); // Here!
  expect(store.state.counter).toBe(4);
});
```

Note: If you prefer not to worry about whether actions under test are synchronous or asynchronous,
you can always use `dispatchAndWait` instead of `dispatch`. It works in both cases.

<hr></hr>

This concludes our review of the basics of Kiss.
However, if you want to become an advanced Kiss user, continue reading the next sections.
The next one will cover advanced topics related to actions.

## Dispatching Actions

As [previously discussed](./store-and-state#immutable-state), the store state is **immutable**.

The only way to change the store **state** is by dispatching **actions**.
The action reducer returns a new state, that replaces the old one.

## Dispatch (one action)

```tsx
// Dispatch an action
store.dispatch(new Increment());
```

In more detail, this is what Kiss does when you dispatch an action:

1. The current state is injected into the action object.
2. The action reducer (its `reduce()` function) is called.
3. The reducer returns a new, modified state.
4. The new state replaces the old one.
5. All components that use the state are rebuilt.

## Dispatch all (multiple actions)

If you want to dispatch more than one action in **parallel** at a time, you can use `dispatchAll`.
Using `dispatchAll` is the same as calling `dispatch` multiple times:

```tsx
// Dispatch multiple actions
store.dispatchAll([new Increment(), new LoadText()]);

// Same as dispatching an action multiple times
store.dispatch(new Increment());
store.dispatch(new LoadText());
```

Note it only makes sense to say the actions will run in parallel when they are asynchronous.
If they are sync, they will run one after the other, naturally.

## Dispatch and wait

You can use `dispatchAndWait` to dispatch an action and get back a promise that resolves when the
action finishes. This works with both sync and async actions:

```tsx
// Dispatch an action and wait for it to finish
await store.dispatchAndWait(new Increment());
```

The state change from the action's reducer will have been applied when the promise
resolves, and it will return the action **status**,
which you can use to check if the action was successful or not.

For example, suppose you want to dispatch an action,
but only after a previous action has finished successfully:

```tsx
var status 
  = await store.dispatchAndWait(new BuyAction('IBM'));

if (status.isCompletedOk()) {
  await store.dispatch(new UpdateBalanceAction());
}
```

The status is also useful in tests, to make sure some action is successful:

```tsx
var status = await store.dispatchAndWait(new MyAction());
expect(status.isCompletedOk()).toBe(true);
```

Or to make sure some action throws an error:

```tsx
var status = await store.dispatchAndWait(new MyAction());
expect(status.originalError).toBeInstanceOf(UserException);
```

## Dispatch and wait all

Use `dispatchAndWaitAll` to dispatch the given actions in parallel, applying their reducers,
and possibly changing the store state.

The actions may be sync or async. You'll get a `Promise` that resolves when **all** actions finish.
In other words, it waits for the slowest action to finish.
The state change from all action's reducers will have been applied when the Promise resolves.

```tsx
await store.dispatchAndWaitAll([
  new BuyAction('IBM'), new SellAction('TSLA')
]);
```

Note `dispatchAndWaitAll` returns the same list of actions you provided,
so that you can instantiate them inline, but still get a list of them.
That's usually only useful for testing. For example:

```tsx
let [buyAction, sellAction] 
  = await store.dispatchAndWaitAll([
      new BuyAction('IBM'), new SellAction('TSLA')
]);

// Test that buyAction threw an error 
expect(buyAction.status.originalError).toBeInstanceOf(UserException);

// Test that sellAction completed successfully 
expect(sellAction.status.isCompletedOk()).toBe(true)); 
```

## Dispatch sync

Using `dispatchSync` is exactly the same as using the regular `dispatch`,
except for the fact it will throw a `StoreException` if the action is **asynchronous**.

Note an action is async if any of its `reduce()` or `before()` methods return a `Promise`.

The only use for `dispatchSync` is when you need to guarantee (in runtime) that your
action is **sync**, which means the state gets changed right after the dispatch call.

Making sure some action you're dispatching is synchronous is usually not a very useful feature,
but it's there if you need it, especially for testing.
             
## Actions can dispatch other actions

You can use dispatch actions from inside other actions. The dispatch functions are available
in the action object, so you can call them directly, by using `this.dispatch()` etc.

For example:

```dart
class LoadTextAndIncrement extends Action {

  async reduce() {
  
    // Dispatch and wait for the action to finish   
    await this.dispatchAndWait(new LoadText());
    
    // Only then, increment the state
    return (state) => state.copy({count: state.count + 1});  
  }
}
```

<hr></hr>

## Hooks To Dispatch Actions

You can use the provided hooks to dispatch actions from inside React components.
                                                                                
## useDispatch etc

You can directly use 
hooks `useDispatch`, `useDispatchAll`, `useDispatchAndWait`
and `useDispatchAndWaitAll`.

For example:

```tsx
function MyComponent() { 
  const dispatch = useDispatch();  

  return (
    <Button onClick={() => dispatch(new LoadText())}> 
      Click me! 
    </Button>
  );
};
```

## useStore

Alternatively, getting a store reference with `useStore` also allows you to dispatch actions
with `store.dispatch`, `store.dispatchAll`, `store.dispatchAndWait` and `store.dispatchAndWaitAll`.

For example:

```tsx
function MyComponent() { 
  const store = useStore();  

  return (
    <Button onClick={() => store.dispatch(new LoadText())}> 
      Click me! 
    </Button>
  );
};
```

## Provide The Store

To provide the store [you just created](./store-and-state) to all your app,
import the `StoreProvider` component from `kiss-for-react` and wrap your app with it.

Note your code should have a **single** store provider, at the top of your component tree.

Then, pass the **store** as a prop to the `StoreProvider`.

```tsx title="index.tsx"
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

const rootElement = document.getElementById("root")!;
const root = ReactDOM.createRoot(rootElement);

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```

```tsx title="App.tsx"
import React from "react";
import { createStore, StoreProvider } from "kiss-for-react";
import State from "./State";

const store = createStore<State>({ initialState: ... });

function App() {
  return (
    <StoreProvider store={store}>
      <AppContent />
    </StoreProvider>
  );
};
```

## Try it out

**[Live Example](https://codesandbox.io/s/w8djq4)**

<hr></hr>

Next, let's see how to access the store's state from any component.

## Store And State

The first thing you need to do to use Kiss is to create a **store**,
which is a centralized place to hold all your application **state**.

You can create a store by using the `createStore` function,
or with `new Store()`:

```tsx
// Using createStore
const store = createStore<State>();

// Using new Store
const store = new Store<State>(); 
```

## Initial state

When you create the store you can provide a few parameters.
Most are optional, but you must at least provide the **initial state**:

```tsx
const store = createStore<State>({ initialState: ... });
const store = new Store<State>({ initialState: ... }); 
```

### State as a number

Let's start as simple as possible.
In this first example, the state is just a number of type `number`,
and the initial state is `0`:

```tsx
const store = createStore<number>({
  initialState: 0,
});
```

### State as a plain JavaScript object

In this second example, the state is a plain JavaScript object.
If we use TypeScript, we can define its type like this:

```tsx
type State = {
  counter: number;
};
```

The initial state is an object with counter zero:

```tsx
const store = createStore<State>({
  initialState: {
    counter: 0,
  },
});
```

### State as an ES6 class

One difference between Kiss and most other JavaScript state management solutions
is that Kiss plays well with ES6 classes.

Some developers don't like JavaScript classes, stating they are not "real classes"
but simply syntactic sugar over prototypes.

However, it's precisely that syntactic sugar that Kiss makes use of,
with the sole goal of making your code more organized and easier to navigate.

It's not important you learn or understand class features like inheritance or polymorphism.
You can use them as simple namespaces, in the way prescribed in this documentation,
and you'll be fine.

You'll see they allow reduced boilerplate, and allow you to navigate between actions and reducers
with a simple click, in IDEs like VS Code and IntelliJ.

Also note, Kiss can serialize ES6 classes just fine.

In this third example, the state is of type `State`, which is a **class** we'll create.
It could contain all sorts of information, but in this case, it's just a number counter:

```tsx
class State {
  constructor(public readonly counter: number = 0) {}
}
```

The initial state is an instance of this class: `new State(0)`:

```tsx
const store = createStore<State>({
  initialState: new State(0),
});
```

## Choice

As shown above, your state can be composed of both plain JavaScript (or TypeScript) objects,
or ES6 classes. Feel free to use the one you prefer.

Most examples in this documentation use ES6 classes as state,
but I also [show examples with objects](../tutorial/plain-javascript-obj).

I personally prefer using ES6 classes as state,
because I find them very readable, easy to use,
and they make it trivial to create and change **immutable state**,
without the need for libraries like [Immer](https://www.npmjs.com/package/immer).

## Immutable state

In Kiss, your state must be **immutable**.

In other words, when you want to change the state, you must create a new state object with the
desired changes, instead of modifying the existing one.

For example, this is a **mutable** state class with an `increment` method:

```tsx
class State {
  constructor(public counter: number = 0) {}

  increment() {
    this.counter++;
  }
}
```

The above state class **cannot** be used with Kiss, because it's mutable.
Instead, use an immutable version, where the `increment` method returns a new state:

```tsx
class State {
  constructor(public readonly counter: number = 0) {}

  increment() {
    return new State(this.counter + 1);
  }
}
```

:::tip

If you decide to use plain JavaScript objects, you may want to use a library
like [Immer](https://www.npmjs.com/package/immer) to help you with immutability.

:::

<hr></hr>

Now that we know how to create the state and the state,
let's see next how to make them available to your component tree.

## User Exceptions

Actions that fail can simply [throw errors](../advanced-actions/errors-thrown-by-actions).

Those errors can be of any type, but in this page we'll discuss a special type of
error called **`UserException`**, which is provided natively by Kiss.
It represents errors that are **not bugs** in the code, 
but rather something that the user can fix.

In other words, if something wrong happens in an action,
you can `throw new UserException('Some suitable error message')` to automatically
open a dialog and show a message to the user.

## Example

Pretend a user is transferring money in your app,
but the user typed `$0.00` as the amount to transfer.
This is an error, but you don't want to log this as an app bug.
Instead, you want to show a message to the user saying _"You cannot transfer zero dollars"_.

This is a perfect use case for `UserException`:

```ts
class TransferMoney extends Action {
  constructor(private amount: number) {}

  reduce() {
    if (this.amount === 0) 
      throw new UserException('You cannot transfer zero dollars.');
    
    return state.copy({
      cashBalance: state.cashBalance - this.amount
    });    
  }
}
```

As another example, suppose you want to save the user's name,
but you only accept names with at least 4 characters.
If the user types a name with less than 4 chars, you want to show an error message to the user.
You also want to show an error message if the server failed to save the user:

```ts
class SaveUser extends Action {
  constructor(private name: string) {}

  async reduce() {
    if (this.name.length < 4) 
      throw new UserException('Name must have at least 4 letters.');
    
    await this.saveUser(this.name);
    return (state) => state.copy({user: state.user.copy({name: this.name}});
  }
  
  async saveUser(name: string) {
    const response = await axios.post('/api/user', { name });
    if (response.status !== 200) {
      throw new UserException('Something went wrong when saving the user');
    }
  }    
}
```

## Error queue

When an action causes a `UserException`, it automatically goes into a special error queue in the
store. From there, an "error widget" can show these errors to the user, one at a time.

Your Team Lead should set up the error widget once for everyone. Regular developers only need to
throw `UserException`s in their daily work.

You can decide how the error widget looks. It could be a dialog box with the error message, a toast
notification, a list of errors shown somewhere, or something else.

Creating your own widget for the error queue is a more advanced topic. For now, we'll focus on
using the built-in error dialog provided by Kiss.

## Show error messages in a dialog

Kiss automatically opens a dialog to show the `message` of all the `UserException` errors
thrown by actions. For this to work, however, you must set up the desired dialog,
toast, or other suitable UI element.

This is done by providing the `showUserException` parameter, when you create the store:

```tsx
const store = createStore<State>({
  initialState: new State(),
  showUserException: userExceptionDialog, // Here!
});
```

For example, the following is a possible `userExceptionDialog` function that opens a dialog with
the error `message` thrown by the action:

```tsx
import { Button, Dialog, DialogActions, DialogContent } from '@mui/material';
import { createRoot } from "react-dom/client";

// Alternative 1: Using a browser dialog
const userExceptionDialog: ShowUserException =
  (exception, count, next) => {
    let message = exception.title ? `${exception.title} - ${exception.message}` : exception.message;
    window.alert(message);
    next();
  };
  
// Alternative 2: Using the MUI library (mui.com) 
export const userExceptionDialog: ShowUserException = (exception: UserException, _count: number, next: () => void) => {
  const container = document.getElementById('dialog-root');
  if (!container) return;
  const root = createRoot(container!);
  const closeDialog = () => {
    root.unmount();
    next();
  };
  root.render(
    <Dialog open={true} onClose={closeDialog}>
      <DialogContent>
        <p>{exception.title || 'Error'}</p>
        <p>{exception.message}</p>
      </DialogContent>
      <DialogActions>
        <Button onClick={closeDialog}>OK</Button>
      </DialogActions>
    </Dialog>
  );
};
```

```tsx 
import { Alert } from 'react-native';

export const userExceptionDialog: ShowUserException =
  (exception, count, next) => {
    Alert.alert(
      exception.title || exception.message,
      exception.title ? exception.message : '',
      [{text: 'OK', onPress: (_value?: string) => next()}]
    );
  };
```

## Wait Fail Succeed

A common pattern in app development involves having a process that can either succeed or fail.
You want to display a spinner during the process, show the result when it completes,
and present an error message if it fails.

These are called "process phases":

* **Waiting**: The process is currently running.
* **Failed**: The process failed with an error.
* **Succeeded**: The process succeeded.

In Kiss, these processes start when actions are dispatched, which means we need a way to
know if an action is currently being processed, if it just failed, and eventually show an error.

Thankfully, this is very easy to do with Kiss, by using the following functions:

* `isWaiting(actionType)`: Is true if the given action type is currently being processed.
* `isFailed(actionType)`: Is true if the given action type just failed.
* `exceptionFor(actionType)`: Returns the exception that caused the action to fail.
* `clearExceptionFor(actionType)`: Clears the exception that caused the action to fail.

## In actions

In actions, we have direct access to the process phase functions above by
using `this.isWaiting`, `this.isFailed`, `this.exceptionFor` and `this.cearExceptionFor`.

For example, suppose we want to create a `SellAction` action that sells a stock.
However, if there is already a `SellAction` or `BuyAction` action currently running,
we want to show an error message instead. This is how you can do it:

```dart    
class SellAction extends Action {
  constructor(public stock: string) { super(); }

  async reduce() {
  
    // Make sure we're not in the middle of another sell or buy process 
    if (this.isWaiting(SellAction) 
        || this.isWaiting(BuyAction)) {
      throw UserException('Please wait for the current order to complete.');
    }
    
    // Only then, post the sell order to the backend
    let amount = await postSellOrder(this.stock);    
    
    return (state) => 
      state.copy({
        stocks: state.stocks.setAmount(this.stock, amount)
      });
  }
}
```

## In components

In components, we have access to the process phase functions by using the
hooks `useIsWaiting`, `useIsFailed`, `useExceptionFor` and `useClearExceptionFor`.

We have already previously seen how to read the state and dispatch actions from components:

```dart    
function MyComponent() {

  const state = useAllState(); 
  
  return (
    <div>
      <p>Counter: {state.counter}</p>
      <button onClick={() => store.dispatch(IncrementAction())}>Increment</button>
    </div>
  );
};
```

Now, let's see how to show a spinner while an action is being processed, and show an error message.

## Show a spinner

Hook `useIsWaiting(actionType)` returns true if the given action type is currently being
processed. By using this hook, you can show a spinner while an action is being processed:

```dart
function MyComponent() {

  const isWaiting = useIsWaiting(IncrementAction);
  const state = useAllState(); 
    
  return (
    <div>
      {
      isWaiting 
        ? <CircularProgress /> 
        : <p>Counter: {state.counter}</p>
      }
    </div>
  );
};
```

## Show an error message

Hook `useIsFailed(actionType)` returns true if the given action type just failed.
By using this hook, you can show an error message when an action fails:

```dart
function MyComponent() {

  const isFailed = useIsFailed(IncrementAction);
  const state = useAllState(); 
    
  return (
    <div>
      {
      isFailed 
        ? <p>Loading failed...</p> 
        : <p>Counter: {state.counter}</p>
      }
    </div>
  );
};
```

If the action failed with a `UserException`, you can get this error by doing
`let error = useExceptionFor(actionType)` and then get the error message
to eventually show it in the UI.

```dart
function MyComponent() {

  const isFailed = useIsFailed(IncrementAction);
  const exception = useExceptionFor(IncrementAction);
  const state = useAllState(); 
    
  return (
    <div>
      {
      isFailed 
        ? <p>Loading failed: {exception.message}</p>
        : <p>Counter: {state.counter}</p>
      }
    </div>
  );
};
```

## Combining isWaiting and isFailed

Let's suppose we've got an async action that loads some text from a server.
You can show a spinner while the action is being processed,
and show an error message if the action fails:

```tsx
function MyComponent() {

  const isWaiting = useIsWaiting(LoadText); 
  const isFailed = useIsFailed(LoadText);  
  const state = useAllState();  
  
  if (isWaiting) return <CircularProgress />
  if (isFailed) return <p>Loading failed...</p>;
  return <p>{state.someText}</p>;
}
```

Now let's repeat the previous code, but add a button that retries the action:

```tsx
function MyComponent() {

  const isWaiting = useIsWaiting(LoadText); 
  const isFailed = useIsFailed(LoadText);  
  const state = useAllState();  
  const store = useStore();
  
  if (isWaiting) return <CircularProgress />
  
  if (isFailed) return (
    <div>
      <p>Loading failed...</p>
      <button onClick={() => store.dispatch(LoadText())}>Retry</button>    
    </div>
  );
  
  return <p>{state.someText}</p>;
}
```

As soon as the user presses the retry button, the spinner will be shown again, and the
error message will be cleared. This happens because the error message is cleared automatically
when the action is dispatched again.

You could always clear the error message explicitly by defining
`let clearExceptionFor = useClearExceptionFor();` and then calling `clearExceptionFor(LoadText)`,
but it's not necessary to do so before dispatching the action again.

<hr></hr>

You now know the basics of Kiss: 
How to create and read the state, dispatch actions to change the state,
run asynchronous actions, show spinners when actions are running, and error messages when they fail.
That is enough for you to be productive with Kiss, and create your own apps with it.

But before we enter the advanced features, let's review the basics by implementing a few simple
counter apps.

---

# Advanced Actions

## Aborting The Dispatch

You may override the action's `abortDispatch()` function to completely prevent
running the action under certain conditions.

:::warning

This is a complex power feature that you may not need to learn.
If you do, use it with caution.
:::

In more detail, if function `abortDispatch()` returns `true`,
the action will not be dispatched: `before`, `reduce` and `after` will not be called.

# Example

```dart
class UpdateUserInfo extends Action {

  // If there is no user, the action will not run.
  abortDispatch() {
    return state.user === null;
  }

...
```

## Creating a base action

You may modify your [base action](./base-action-with-common-logic) to make it easier
to add this behavior to multiple actions:

```ts
export abstract class Action extends KissAction<State> {

  allowWhenLoggedOut = false;   
  
  abortDispatch() {
  
    // The action should abort if it's not allowed to run while 
    // the user is logged out, and the user is indeed logged out. 
    let shouldAbort = !allowWhenLoggedOut && (state.user === null);        
    
    if (shouldAbort) {      
      navigateToHomePage();              
      return true; // Abort the action.      
    }        
    else {           
      return false; // Don't abort the action.
    } 
  }  
}
```

Now, only actions with `allowWhenLoggedOut = true` will be able to run
when the user is logged out.

```ts
// This action to log in the user should 
// be able to run when the user is logged out.
class LogIn extends Action {
  allowWhenLoggedOut = true;         
  async reduce() { ... }
}

// This action that allows the user to send a message 
// should NOT be able to run when there is no user.
class SendMessage extends Action {           
  async reduce() { ... }
}
```

## Action Features

You can add **features** to your actions, to accomplish common tasks:

* [nonReentrant](#nonreentrant)
* [retry](#retry)
* [checkInternet](#checkinternet)
* [debounce](#debounce)
* [throttle](#throttle)
* [ignoreOld](#ignoreold)
* [optimisticUpdate](#optimisticupdate)

## nonReentrant

To prevent an action from being dispatched while it's already running,
add `nonReentrant = true` to your action.

```tsx
class LoadText extends Action { 
  nonReentrant = true;
   
  reduce() { ... }
}
```

## retry

To retry an action a few times with exponential backoff, if it fails,
add the `retry` property to your action class.

```tsx
class LoadText extends Action {   
  retry = {on: true}

  reduce() { ... }
}
```

The retry parameters are:

* Initial Delay: The delay before the first retry attempt.
* Multiplier: The factor by which the delay increases for each subsequent retry.
* Maximum Retries: The maximum number of retries before giving up.
* Maximum Delay: The maximum delay between retries to avoid excessively long wait times.

And their default values are:

* `initialDelay` is `350` milliseconds.
* `multiplier` is `2`, which means the default delays are: 350 millis, 700 millis, and 1.4 seg.
* `maxRetries` is `3`, meaning it will try a total of 4 times.
* `maxDelay` is `5000` milliseconds (which means 5 seconds).

You can change one or more of the default values.

```tsx
class LoadText extends Action {

  retry = {
    initialDelay: 350, // Millisecond delay before the first attempt
    maxRetries: 3,     // Number of retries before giving up
    multiplier: 2,     // Delay increase factor for each retry
    maxDelay: 5000,    // Max millisecond delay between retries
  }
   
  reduce() { ... }
}
```

If you want to retry unlimited times, make `maxRetries` equal to: `-1`:

```ts
class LoadText extends Action {
   retry = {maxRetries: -1};
}
```

Notes:

* If you `await dispatchAndWait(action)` and the action uses unlimited retries,
  it may never finish if it keeps failing. So, be careful when using it.

* If the `before` method throws an error, the retry will NOT happen.

* The retry delay only starts after the reducer finishes executing. For example, if the
  reducer takes 1 second to fail, and the retry delay is 350 millis, the first retry will
  happen 1.35 seconds after the first reducer started.

* When the action finally fails, the last error will be rethrown, and the previous ones
  will be ignored.

* For most actions that use `retry`, consider also making them non-Reentrant to avoid
  multiple instances of the same action running at the same time:

  ```ts
  class MyAction extends KissAction<State> {
     retry = {on: true}
     nonReentrant = true;
  }
  ```

* Keep in mind that all actions using the `retry` feature will become asynchronous, even
  if the original action was synchronous.

* If necessary, you can know the current _attempt number_ by using `this.attempts`.

## checkInternet

Adding `checkInternet = { dialog: true }` to your action ensures it only runs with internet. Otherwise, an **error dialog** prompts users to check their connection:

```tsx
class LoadPrices extends Action {  
  
  checkInternet = { dialog: true } 
   
  async reduce() { ... } 
}   
```

Use `checkInternet = { dialog: false }` if you don't want to open a dialog.
Instead, you can display some information in your widgets:

```tsx
function MyComponent() {
  const isFailed = useIsFailed(LoadPrices);

  return (
    <div>
      {isFailed ? <p>No Internet connection</p> : null}
    </div>
  );
};   
```

In web environments, the default is checking the availability of the internet using `navigator.onLine`. However, `navigator.onLine` is not very useful, as it only tells you if there's a local connection, and not whether the internet is accessible. Also, this only really works for the web.

In other words, to make `checkInternet` work properly you will need to provide your own logic to check for internet availability.
To that end, you must override the `hasInternet` method of your base action, and then provide your own logic to determine if the internet is available or not.

Alternatives are using the [NetInfo](https://www.npmjs.com/package/@rescript-react-native/netinfo) package for React Native,
or using the [is-online](https://www.npmjs.com/package/is-online) package
for Node.js and the browser.

For an example using NetInfo, first add it to your `package.json`:

```json
"dependencies": {
   "@react-native-community/netinfo": "^11.4.1"
}
```

Then, import it in your base action, and override the `hasInternet()` method:

```typescript
import NetInfo from '@react-native-community/netinfo';

export abstract class Action extends KissAction<State> {

   protected hasInternet(): Promise<boolean> {
      return NetInfo.fetch().then(state => state.isConnected ?? true);
   }
}
```

## debounce

To limit how often an action occurs in response to rapid inputs,
add something like `debounce = 300` to your action class,
where `300` is the number of milliseconds.

For example, when a user types in a search bar, debouncing ensures that not every keystroke
triggers a server request. Instead, it waits until the user pauses typing before acting.

```tsx
class SearchText extends Action {
  constructor(public searchTerm: string) { super(); }
  
  debounce = 300 // Milliseconds
   
  async reduce()  {      
    let result = await loadJson('https://example.com/?q=', searchTerm);
    return (state) => state.copy({searchResult: result});
  }   
}
```

> Important: _this feature is still in development. It should be available soon._

## throttle

To prevent an action from running too frequently,
add something like `throttle = 5000` to your action class,
where `5000` means 5 seconds.

After the action runs it's considered _fresh_, and it won't run
again for a set period of time, even if you dispatch it.
After this period ends, the action is considered _stale_ and is ready to run again.

```tsx
class LoadPrices extends Action {  
  
  throttle = 5000 // Milliseconds
   
  async reduce()  {      
    let result = await loadJson('https://example.com/prices');
    return (state) => state.copy({prices: result});
  } 
}
```

> Important: _this feature is still in development. It should be available soon._

## ignoreOld

If some action that performs a slow async process may be dispatched multiple times in a rapid
sequence, you may want to ignore its result if a newer action of the same type was
already dispatched. This
avoids [race conditions](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect)
where the result of an older action overwrites the result of a newer one.

In Kiss this problem can be easily solved by adding `ignoreOld = true` to
your action class:

```tsx
class LoadText extends Action {  
  
  ignoreOld = true
   
  async reduce()  {      
    let response = await fetch("https://dummyjson.com/todos/random/1");        
    let jsonResponse = await response.json();
    let text = jsonResponse[0].todo;     
    return (state) => state.copy(text: text));
  }   
}
```

In the above example, if the action gets called twice or more before the first one finishes,
only the last one will be considered, and the previous ones will be ignored.

In more detail, the returned state from the reducer will only be applied if the action is the
most recent one dispatched. If it's not, the state will be discarded.

Note that we are not aborting the requests, but simply ignoring the reducer result.
This is fine for most cases.

If we actually want to abort the requests, we need to use
an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal)
by calling `this.getAbortController()` in the action, and then using that controller to
abort the request:

```tsx
class LoadText extends Action {  
  
  ignoreOld = true
   
  async reduce()  {
    let abortController = this.getAbortController(); // Here!    
    
    let response = await fetch("https://dummyjson.com/todos/random/1", {
      signal: abortController.signal, // Here!
    });
            
    let jsonResponse = await response.json();
    let text = jsonResponse[0].todo;     
    return (state) => state.copy(text: text));
  }   
}
```

Kiss will automatically abort the previous requests by calling `abortController.abort()`
when a new action is dispatched.
When this happens, the `fetch()` promise will reject with an `AbortError`,
but this error is caught by Kiss and ignored, so you don't need to worry about it.

If instead of `fetch` you use **Axios**, it also works:

```tsx
let response = await axios.get('https://dummyjson.com/todos/random/1', {
  signal: abortController.signal
});
```

Note: The `ignoreOld` feature is not compatible with the [nonReentrant](#nonreentrant) feature,
because they are opposite concepts. While `nonReentrant` aborts the newer actions
if an older one is running, `ignoreOld` aborts the older actions if a newer one is running.
If you try to use both at the same time, an error will be thrown.

> Important: _this feature is still in development. It should be available soon._

## optimisticUpdate

To provide instant feedback on actions that save information to the server,
this feature immediately applies state changes as if they were already successful,
before confirming with the server.

If the server update fails, the change is rolled back and, optionally,
a notification can inform the user of the issue.

```tsx
class SaveName extends Action {  
  
  optimisticUpdate = { ... } 
   
  async reduce() { ... } 
}
```

> Important: _this feature is still in development. It should be available soon._

## Action Status

All actions have a `status` property of type `ActionStatus`,
that provides information about the action execution.

You can read the action status at any point in time.
The status object is immutable, so it will
always reflect the state of the action at the time you read it.

```ts
let action = new MyAction();

let status = action.status;
console.log(status);

store.dispatchSync(action);

let status = action.status;
console.log(status);
```

## isCompleted

Use `action.status.isCompleted` to check if an action has completed dispatching, 
either with or without errors.

- **true**: the action's `after()` function already ran.

- **false**: the action is still running, or hasn't been dispatched yet.

## isCompletedOk

Use `action.status.isCompletedOk` to check if an action has completed dispatching without errors.

- **true**: none of the `before()` or `reduce()` functions have thrown an error.
  This indicates that the `reduce()` function completed and returned a result (even if
  the result was `null`). The `after()` function also already ran.

- **false**: the action is still running, hasn't been dispatched yet, or completed with errors.

## isCompletedFailed

Use `action.status.isCompletedFailed` to check if the action has completed dispatching with errors.

- **true**: the action has completed, and the `after()` function already ran.
  Either the `before()` or the `reduce()` functions have thrown an error. It indicates that the
  reducer did **not** complete, and could not have returned a value to change the state.

- **false**: the action is still running, hasn't been dispatched yet, or completed without errors.

An example:

```ts
let action = new MyAction(); 
await store.dispatchAndWait(action);
console.log(action.isCompletedOk);
```

Better yet, you can get the status directly from the `dispatchAndWait` function:

```ts       
let status = await store.dispatchAndWait(MyAction());
console.log(status.isCompletedOk);
```

## Getting the action error

If the action finished with an error, you can get the original error:

```ts
let error = action.status.originalError;
```

That's called an "original error" because it's the error that was originally thrown by the
action's `before()` or `reduce()` functions.

In case this error is later changed by the action's `wrapError()` function,
you can also get the "wrapped error":

```ts
let error = action.status.wrappedError;
```

## Up until which point did the action run?

You can also use the status to check if the action has finished running
the `before()`, `reduce()`, and `after()` functions:

```ts
let status = await dispatchAndWait(MyAction(info));
console.log(action.status.hasFinishedMethodBefore);
console.log(action.status.hasFinishedMethodReduce);
console.log(action.status.hasFinishedMethodAfter);
```

## Use cases

The action status is useful mainly in testing and debugging scenarios.
In production code, you are usually more interested in the state change that the action caused,
rather than the action status.

However, one possible use case in production is taking some action only if the action completed.

As an example, suppose you want to save some info,
and you want to leave the current screen if and only if the save process succeeded.

You could have the following save action:

```ts
class SaveAction extends Action {     
  async reduce() {
    let isSaved = await saveMyInfo(); 
    if (!isSaved) throw new UserException('Save failed');	 
    return null;
  }
}
```

Then, in your widget, you can write:

```ts
let status = await dispatchAndWait(SaveAction(info));
if (status.isCompletedOk) navigateAwayFromTheScreen();  
```

## Base Action With Common Logic

In Kiss, all actions must extend `KissAction<State>`.
For example:

```tsx
import { KissAction } from 'kiss-for-react';
import { State } from 'State';

class Increment extends KissAction<State> {
  reduce() {
    return (state: State) => state.increment();
  }
}
```

In all the code I show in this documentation, you'll see I usually write `extend Action`
instead of `extend KissAction<State>`.

This is because I'm assuming you have previously defined your own abstract base action class
called simply `Action`, that itself extends `KissAction<State>`. Then, you may have all your
actions extend this `Action` class instead.

This is how you would define the `Action` class:

```tsx 
import { KissAction } from 'kiss-for-react';
import { State } from 'State';

export abstract class Action extends KissAction<State> { }
```

Remember this is optional, but recommended. The reason to do this is twofold:

* First, you'll avoid writing `extends KissAction<State>` in every action class.
  Now, you'll need to write `extends Action` instead, which is simpler.

* And second, to have a common place to put any **common logic**
  that all your actions should have access to. More on that later.

# Common logic

Suppose we have the following app state:

```ts
class State {
  constructor(
    public items: Item[] = [], 
    public selectedItem: Item | null = null
  ) {}
}

class Item {
  constructor(public id: string) {}  
}
```

And then we have an action, which selects an item by `id`:

```ts
class SelectItem extends Action {
  constructor(public id: number) { super(); }

  reduce() {
    let item = state.items.find(item => item.id === this.id);
    if (item === undefined) throw new UserException('Item not found');
    return state.copy({selectedItem: item});
  }
}
```

You would use it like this:

```ts
var item = new Item('A'); 
dispatch(new SelectItem(item));
```

Now, suppose we have a lot of actions that need to access the `items` and `selectedItem` properties.
We could add getters and selectors to the base `Action` class:

```ts
abstract class Action extends KissAction<State> {
  
  // Getters shortcuts
  get items(): Item[] { return this.state.items; }
  get selectedItem(): Item { return this.state.selectedItem; }

  // Selectors
  findById(id: number): Item | undefined { return this.items.find(item => item.id === id); }
  searchByText(text: string): Item | undefined { return this.items.find(item => item.text.includes(text)); }  
  get selectedIndex(): number { return this.selectedItemId !== null ? this.items.findIndex(item => item.id === this.selectedItemId) : -1; }
}
```

And then your actions have an easier time accessing the store state:

```ts
class SelectItem extends Action {
  constructor(public id: number) { super(); }

  reduce() {
    let item = this.findbyId(this.id); // Here!
    if (item === undefined) throw new UserException('Item not found');
    return state.copy({selectedItem: item});
  }
}
```

The difference above is that, instead of writing:

```ts
let item = state.items.find(item => item.id === this.id); 
```

You can simply write:

```ts
let item = this.findbyId(this.id); 
```

It may seem a small reduction of boilerplate, but it adds up.

In practice, your base action class may end up containing a lot of elaborate "selector functions",
which then can be used by all your actions.

The only requirement is that your actions now
extend `Action` instead of `KissAction<State>`.

## Before And After The Reducer

Suppose you want to prevent the user from touching the screen, while an async action is running.
This means adding a modal barrier before the action starts, and removing it after the action ends.

Or suppose you want to check some precondition when an action is dispatched,
and maybe throw an error if the precondition is not met.

It's indeed common to have some side effects both before and after the reducer runs.
To help you with these use cases, you may override your action functions `before()`
and `after()`, which run respectively before and after the reducer.

:::info

Implementing your action's `reduce()` function is mandatory,
but `before()` and `after()` are optional.
Their default implementation simply does nothing.

:::

## Before

The `before()` function runs before the reducer.

To run synchronously, return `void`.
To run it asynchronously, add `async` and return `Promise<void>`:

```ts
// Sync
before(): void {
  ...
}

// Async
async before(): Promise<void> {
  ...
}
```

What happens if `before()` throws an error? In this case, `reduce()` will **not** run.
This means you can use `before()` to check any preconditions,
and maybe throw an error to prevent the reducer from running. For example:

```ts
// If there is no internet connection, throws a UserException 
// to show a dialog and prevent the reducer from running.
async before(): Promise<void> {
  if (!(await hasInternetConnection())) 
    throw new UserException('No internet connection');
}
```

:::info

If `before()` returns a promise, then the action becomes async
(its reducer will complete in a later microtask than the dispatch call),
regardless of the `reduce()` function being sync or async.

:::

## After

Function `after()` runs after the reducer. It's always a synchronous function:

```ts
after(): void {
  ...
}
```

Note `after()` is akin to a _finally block_,
since it will always run, even if an error was thrown by `before()` or `reduce()`.
This is important so that it can undo any side effects that were done in `before()`,
even if there was an error later in the reducer.

:::info

Make sure your `after()` function doesn't throw an error.
If it does, the error will be swallowed, but logged with `Store.log()`.
Keep in mind the default logger will print the error to the console,
but you may provide your own `logger` to the `Store` constructor.

:::

## Example

Suppose we have a counter app. When you press the "Increment" button,
it dispatches the `Increment` action, that takes 1 second to increment the counter.
This action adds a dark screen barrier when it starts,
and then removes the barrier when it finishes.

First, we need to create a `BarrierAction`:

```ts
class BarrierAction extends Action {
  constructor(public hasBarrier: boolean) { super(); }

  reduce() {
    return this.state.copy({hasBarrier: this.hasBarrier});
  }
}
```

And then we need a barrier component which occupies the whole screen
and is shown only when `hasBarrier` is true:

```tsx
function Barrier() {
  let hasBarrier = useSelect((state: State) => state.hasBarrier);
  
  return hasBarrier 
    ? <div className="Barrier" /> 
    : <></>;
}
```

```css title="CSS"
.Barrier {
  position: fixed; z-index: 9999;
  top: 0; left: 0; width: 100%; height: 100%;
  background-color: rgba(0, 0, 0, 0.4);  
}
```

After this is set up, you may use `before()` and `after()` to dispatch the `BarrierAction`:

```ts
class Increment extends Action {

  async reduce() {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return (state: State) => this.state.increment();
  }

  before() { this.dispatch(new BarrierAction(true)); }
  after() { this.dispatch(new BarrierAction(false)); }
}
```

**[Live Example](https://codesandbox.io/s/255qf8)**

## Creating a base action

You may also modify your [base action](./base-action-with-common-logic) to make it easier
to add this behavior to multiple actions:

```ts
import { KissAction } from 'kiss-for-react';
import { State } from 'State';

export abstract class Action extends KissAction<State> {
  barrier = false;  
  before() { if (this.barrier) this.dispatch(new BarrierAction(true)); }
  after() { if (this.barrier) this.dispatch(new BarrierAction(false)); }  
}
```

Now you can add `barrier = true;` in all your desired actions,
to provide `before()` and `after()` by default:

```ts
class Increment extends Action {

  barrier = true;
  
  async reduce() {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return (state: State) => this.state.increment();
  }
}
```

This is the code using the modified `BaseAction` and `Increment` actions:

**[Live Example](https://codesandbox.io/s/vhy8t9)**

## Errors Thrown By Actions

When your action runs, it may encounter problems. Examples include:

* A bug in your code
* A network request fails
* A database operation fails
* A file is not found
* A user types an invalid value in a form
* A user tries to log in with an invalid password
* A user tries to delete a non-existing item

In Kiss, if your action encounters a problem, you are allowed to do the obvious thing
and simply **throw an error**. In this case, we say that the action "failed".

Kiss has special provisions for dealing with errors thrown by actions,
including observing errors, showing errors to users, and wrapping errors into more meaningful
descriptions.

## What happens
                
As previously discussed, your actions can implement the
functions [`before()`](before-and-after-the-reducer#before),
[`reduce()`](../basics/actions-and-reducers#the-reducer),
and [`after()`](before-and-after-the-reducer#after).
This is what happens if an action throws an error:

* **Before**: If an action throws an error in its `before()` function, the reducer will not be
  executed, will not return a new state, and the store state will **not** be modified.

* **Reduce**: If an action throws an error in its `reduce()` function,
  the reducer will stop in its tracks before completing. It will not return a new state,
  and the store state will **not** be modified.

* **After**: The action's `after()` function will **always** be called, no matter if the other
  two functions threw errors or not. For this reason, if you need to clean up some action
  resources, you should do it here.

:::tip

And if at any point you need to know if and how the action failed,
you can check its [action status](./action-status).

:::

<br></br>

Now let's create an example to help us think about error handling in actions.

Here is a `LogoutAction` that checks if there is an internet connection,
in which case it deletes the app database, sets the store to its initial state,
and navigates to the login screen:

```ts
class LogoutAction extends Action {

  async reduce() {
    await this.checkInternetConnection();
    await this.deleteDatabase();
    this.dispatch(new NavigateToLoginScreenAction());
    
    return (state) => State.initialState();
  }
  
  async checkInternetConnection() { ... }  
  async deleteDatabase() { ... }
}
```

In the above code, suppose the `checkInternetConnection()` function checks if there is an
internet connection. If there isn't, it throws an error.
Here is the code
for <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine">React</a>
and <a href="https://www.npmjs.com/package/@react-native-community/netinfo">React Native</a>:

```ts
async checkInternetConnection() { 
  if (!navigator.onLine) {
    throw new NoInternetConnectionError();
  }  
}
```

```ts
async checkInternetConnection() {
  const state = await NetInfo.fetch();
  if (!state.isConnected) {
    throw new NoInternetConnectionError();
  }
}
```

With this example in mind, let's explore our options.

## Local error handling

If your action throws some error,
you probably want to collect as much information as possible about it.
This can be useful for debugging, or for showing the user a more informative error message.

In the above code, if `checkInternetConnection()` throws an error,
you want to know that you have a connection problem,
but you also want to know this happened during the logout action.
In fact, you want all errors thrown by this action to reflect that.

The solution is overriding your action's **`wrapError()`** function.

It acts as a sort of "catch" statement of the action,
getting all errors thrown by the action.
You if you wish, this function can then return a new error to be thrown.
In other words:

* To modify the error, override the `wrapError()` function and return something.
* To keep the error the same, just return it unaltered, or don't override `wrapError()`.

Usually, you'll want to wrap the error inside another that better describes the failed action,
or contains more information. For example, this is how you could do it in the `LogoutAction`:

```ts
class LogoutAction extends Action {
  async reduce() {
    // ...
  }

  wrapError(error: any) {
    return new LogoutError("Logout failed", error);
  }
}
```

Note the `LogoutError` above includes the original error as a cause, so no information is lost.

## Showing a dialog to the user

Now suppose we want to show a dialog to the user, saying the logout failed, no matter
what the error was.

As [previously discussed](../basics/user-exceptions), 
throwing a `UserException` will automatically show a dialog to the user, 
where the dialog's message is the exception's message.

This is a possible solution, using `try/catch`:

```ts
class LogoutAction extends Action {

  async reduce() {
    try {
      await this.checkInternetConnection();
      await this.deleteDatabase();
    } catch (error) {
      throw new UserException('Logout failed', {hardCause: error});
    }
      
    this.dispatch(new NavigateToLoginScreenAction());    
    return (state) => State.initialState();
  }
}
```

However, you can achieve the same by overriding the `wrapError()` function:

```ts
class LogoutAction extends Action {

  async reduce() {
    await this.checkInternetConnection();
    await this.deleteDatabase();      
    this.dispatch(new NavigateToLoginScreenAction());    
    return (state) => State.initialState();
  }
  
  wrapError(error: any) {
    return new UserException('Logout failed', {hardCause: error});
  }  
}
```

## Creating a base action

You may also modify your [base action](./base-action-with-common-logic) to make it easier
to add this behavior to multiple actions:

```ts
import { KissAction } from 'kiss-for-react';
import { State } from 'State';

export abstract class Action extends KissAction<State> {
  wrapErrorMessage = undefined;  
  
  wrapError(error) {
    if (this.wrapErrorMessage !== undefined)
      return new UserException(wrapErrorMessage(), {hardCause: error});
  }  
}
```

Now you can easily add the `wrapErrorMessage` function in all your desired actions,
to make sure all action errors are wrapped in a `UserException`:

```ts
class LogoutAction extends Action {

  wrapErrorMessage = () => 'The logout failed';
   
  async reduce() {
    await this.checkInternetConnection();
    await this.deleteDatabase();      
    this.dispatch(new NavigateToLoginScreenAction());    
    return (state) => State.initialState();
  }    
}
```

## Global error handling

Third-party code may also throw errors which should not be considered bugs,
but simply messages to be displayed in a dialog to the user.

For example, Firebase may throw some `PlatformException` errors
in response to a bad connection to the server.
In this case, it may be a good idea to convert this error into a `UserException`,
so that a dialog appears to the user with the error message.

There are two ways to do that. One of them we already discussed above:
Just convert it in the action itself
by implementing the optional `wrapError()` function:

```ts
class MyAction extends Action {
  
  wrapError(error: any) {
    if (error instanceof PlatformException 
      && error.code === "Error performing get" 
      && error.message === 'Failed to get document because the client is offline'
      ) {
      return new UserException('Check your internet connection').addCause(error);
    } else { 
      return error;
    }   
  }    
}
```

However, then you'd have to add this code to all actions that use Firebase.

A better way is providing it globally as the `globalWrapError` parameter, when you create the store:

```ts              
const store = createStore<State>(
  initialState: new State(),
  globalWrapError: globalWrapError,
);

function globalWrapError(error :any) {
  if (error instanceof PlatformException 
    && error.code === "Error performing get" 
    && error.message === 'Failed to get document because the client is offline'
    ) {
    return new UserException('Check your internet connection').addCause(error);
  } else { 
    return error;
  }
}    
```

The `globalWrapError` function will be given all errors,
and it's called **after** the action's own `wrapError()` function, if it exists.

It may then return a `UserException` which will be used instead of the original exception.
Otherwise, it just returns the original `error`, so that it will not be modified.
It may also return `null` to disable (swallow) the error.

:::tip

If instead of **returning** an error you **throw** an error inside the `globalWrapError`
function, Kiss will catch this error and use it instead the original error. In other
words, returning an error or throwing an error works the same way. But it's recommended that
you return the error instead of throwing it anyway.

:::

:::info

Don't use the `globalWrapError` to log errors, as you should prefer doing that
in the global `errorObserver` that will be discussed below.

The `globalWrapError` is always called **before** the `errorObserver`.

:::

## Disabling errors

If you want your action to disable its own errors, locally,
the action's `wrapError()` function may simply return `null`.

For example, suppose you want to let all errors pass through, except for errors of
type `MyException`:

```ts
wrapError(error :any) { 
  return (error instanceof MyException) ? null : error;
}
```

If you want this to happen globally, use the `globalWrapError` instead:

```ts
const store = createStore<State>(
  initialState: new State(),
  globalWrapError: globalWrapError,
);

function globalWrapError(error :any) {
  return (error instanceof MyException) ? null : error;
}   
```

## Error observer

An `errorObserver` function can be set during the store creation.
This function will be given all errors that survive the action's `wrapError` and
the `globalWrapError`, including those of type `UserException`.

You also get the `action` and a reference to the `store`. IMPORTANT: Don't use the store to
dispatch any actions, as this may have unpredictable results.

The `errorObserver` is the ideal place to log errors, as you have all the information you may
need, including the `action` that dispatched the error, which you can use to log the action
name, as well as any action properties you may find interesting.

After you log the error, you may then return `true` to let the error throw,
or `false` to swallow it.

For example, if you want to disable all errors in _production_, but log them;
and you want to throw all errors during _development_ and _tests_, this is how you can do it:

```ts
const store = createStore<State>(
  initialState: new State(),
  errorObserver: errorObserver,
);

function errorObserver(error: any, action: Action, store: Store<State>) {

   // In development and tests, we throw the error so that we can 
   // see it in the emulator/console. We also always let UserExceptions 
   // pass through, so that they can be shown to the user.
   if (inDevelopment() || inTests() || (error instanceof UserException)) { 
     return true;
   }
   // In production, we log the error and swallow it by returning false.
   else {
     Logger.error(`Got ${error} in action ${action}.`);
     return false;
   }
}            
```

As you can see, the error observer returns a boolean:

* If it returns `true`, the error will be rethrown after the `errorObserver` finishes.

* If it returns `false`, the error is considered dealt with, and will be "swallowed" (not rethrown).
  This is usually what we want to do in production, after logging the error.

## UserExceptionAction

As [previously discussed](../basics/user-exceptions), the `UserException` is a special type of error
that Kiss automatically catches and shows to the user in a dialog, or other UI of your
choice.

For this to work, you must throw the `UserException` from inside an
action's `before()` or `reduce()` functions. Only then, Kiss will be able to
catch the exception and show it to the user.

However, if you are **not** inside an action, but you still want to show an error dialog to the
user, you may use the provided `UserExceptionAction`.

```ts
dispatch(UserExceptionAction('Please enter a valid number'));
```

This action simply throws a corresponding `UserException` from its own `reduce()` function.

The `UserExceptionAction` is also useful inside of actions themselves,
if you want to display an error dialog to the user,
but you don't want to interrupt the action by throwing an exception.

For example, here an invalid number will show an error dialog to the user,
but the action will continue running and set the counter state to `0`:

```ts
class ConvertAction {
  constructor(private text: string) {}

  reduce() {
    let value = parseInt(this.text);

    if (isNaN(value)) { 
      dispatch(new UserExceptionAction('Please enter a valid number'));
      value = 0;
    }  
    
    return { counter: value };
  }
}
```

## Wrapping The Reducer

You may wrap an action reducer to allow for some pre- or post-processing.

:::warning

This is a complex power feature that you may not need to learn.
If you do, use it with caution.
:::

Actions allow you to define a `wrapReduce()` function,
that gets a reference to the action reducer as a parameter.
If you override `wrapReduce()` it's up to you to call `reduce()` and
return a result.

In `wrapReduce()` you may run some code before and after the reducer runs,
and then change its result, or even prevent the reducer from running.

## Example

Imagine you have a chat application, where you can use the `SendMsg` action
to send messages of type `Msg`.

Each message has an `id`, as well as a `status` field that can be:

* `queued`: message was created in the client
* `sent`: message was sent to the server
* `received`: message was received by the recipient user

The action uses the service `service.sendMessage()` to send the queued message,
and then updates the message status to `sent`:

```ts
class SendMsg extends Action {
  constructor(private msg: Msg) { super(); }      
 
  async reduce() {
    await service.sendMessage(msg);
    return (state: State) => this.state.setMsg(msg.id, msg.copy(status: 'sent'));
  }
}
```

This mostly works, but there is a race condition.
The application is separately using websockets to listen to message updates from the server.
When the sent message is received by the recipient user, the websocket will let the
application know the message is now `received`.

If the message status is updated to `received` by the websocket before `service.sendMessage(msg)`
returns, the message status will be overwritten back to `sent` when the action completes.

One way to fix this, is checking if the message status is already `received` before updating
it to `sent`. In this case, you abort the reducer.

This can be done in the reducer itself, by returning `null` to abort and avoid modifying the state:

```ts
class SendMsg extends Action {
  constructor(private msg: Msg) { super(); }      
 
  async reduce() {    
    await service.sendMessage(msg);
    
    const currentMsg = this.state.getMsgById(msg.id);
    
    if (currentMsg.status === 'received')
      return null;       
    else 
      return (state) => this.state.setMsg(msg.id, msg.copy(status: 'sent'))          
  }
}
```

Another option is using `wrapReduce()` to wrap the reducer:

```ts
class SendMsg extends Action {
  constructor(private msg: Msg) { super(); }      

  async wrapReduce(reduce: () => KissReducer<St>)) {   
      
    // Get the message object before the reducer runs.  
    const previousMsg = this.state.getMsgById(msg.id);
    
    const newState = await reduce();
    
    // Get the current message object, after the reducer runs.
    const currentMsg = this.state.getMsgById(msg.id);
      
    // Only update the state if the message object hasn't changed.  
    return (previousMsg === currentMsg) 
      ? newState 
      : null;
  }
 
  async reduce() {    
    await service.sendMessage(msg);
    return (state) => this.state.setMsg(msg.id, msg.copy(status: 'sent'))            
  }
}
```

## Creating a base action

While wrapping the reducer may seem more work,
you may now modify your [base action](./base-action-with-common-logic) to make it easier
to add this behavior to multiple actions:

```ts
export abstract class Action extends KissAction<State> {
  observedState = undefined;  
  
  async wrapReduce(reduce: () => KissReducer<St>)) {
    if (observedState === undefined) {
      return reduce;
    }        
    
    let oldObservedState = this.observedState(this.state);    
    let newState = await reduce();
    let newObservedState = this.observedState(this.state);    
      
    return (oldObservedState === newObservedState) 
      ? newState 
      : null;
  }  
}
```

Now you can easily add the `observedState` function in all your desired actions,
to make sure the reducer is only applied if the observed state hasn't changed:

```ts
class SendMsg extends Action {
  constructor(private msg: Msg) { super(); }      

  observedState = (state :State) => this.state.getMsgById(msg.id); 
  
  async reduce() {    
    await service.sendMessage(msg);
    return (state) => this.state.setMsg(msg.id, msg.copy(status: 'sent'))            
  }
}
```

---

# Testing

## Debugging The App

Creating both unit tests and integration tests with Kiss is very easy.
If you create a lot of tests, this helps you to avoid bugs in the first place,
and you'll probably spend very little time debugging your app.

However, if you still need to manually debug your app from time to time, here are some tips:

## Viewing the state

At any moment, you can print the current store state to the console,
from inside any **component**:

```ts
const state = useAllState();
console.log(state);
```

Or from inside **actions**:

```ts
console.log(this.state);
```

Or from inside **tests**:

```ts
const store = new Store<State>({ initialState: new State() });
...

console.log(store.state);
```

## Checking actions in progress

Printing the list of actions in progress to the console can be useful for debugging,

You can get an array of the actions that are currently in progress by
using `actionsInProgress()` from inside any **component**:

```ts
const store = useStore();
console.log(store.actionsInProgress());
```

Or from inside **actions**:

```ts
console.log(this.store.actionsInProgress());
```

Or from inside **tests**:

```ts
const store = new Store<State>({ initialState: new State() });
...

console.log(store.actionsInProgress());
```

## State changes

Function `Store.describeStateChange(obj1, obj2)` returns a string
describing only the differences between two given objects.

If you take a snapshot of the store state in different moments,   
you can use this function to print the differences between them to the console.
For example:

```ts
const store = new Store<State>({ initialState: new State() });

let state1 = store.state;
await store.dispatchAndWait(new MyAction());
let state2 = store.state;

console.log(Store.describeStateChange(state1, state2));
```

## Logging state changes to the console

By default, Kiss will use `Store.log()` to print all state changes to
the console, as this may be useful for development, testing and debugging.

Be sure to use `logStateChanges: false` in the store constructor,
to turn it off in **production**, as leaving this on may slow down your application:

```ts
// In production
const store = new Store<State>({
  initialState: new State(),
  logStateChanges: false,
});
```

## Dispatch count

Each time an action is dispatched, Kiss increments an internal counter.
This counter can be retrieved from `store.dispatchCount`. This number may sometimes
be useful for logging and debugging.

## Mocking Actions

Mocks should be used for **testing** purposes, only.

You can mock actions to return specific states, to throw specific errors, 
or even to abort the action dispatch.

## mocks.add

By adding mocks to the store,
whenever an action is dispatched, 
Kiss will check if there is a mock for the action's **type**.
If there is, the corresponding **mock function** will be called
to return a **mock action** that will be dispatched instead.

```ts
store.mocks.add(actionType, (action) => mockAction);
```

The action type to be mocked is set in parameter `actionType` shown above.
The mock function is given as `(action) => mockAction`. It will be called to return a `mockAction`.

However, if `mockAction` is `null` the action will be aborted:
it will not dispatch, and will not change the state or throw any errors.

Let's see a few examples:
                  
**Mocking an action with another action**

```ts
// When IncrementAction() is dispatched, 
// action AddAction(1) will be dispatched instead.
store.mocks.add(IncrementAction, (action) => new AddAction(1));
```  

<br></br>
**Changing an action's value**

```ts
// When an AddAction(value) is dispatched, instead  
// of adding, the value will be subtracted instead.
store.mocks.add(AddAction, (action) => new AddAction(-action.value));
```  

<br></br>
**Aborting an action**

```ts
// When LoadUserAction() is dispatched, it will be aborted.
store.mocks.add(LoadUserAction, null);
```  

<br></br>
**Counting actions**

```ts
let count = 0;
store.mocks.add(IncrementAction, (action) => {
  count++;
  return action;
});

store.dispatch(new IncrementAction());
store.dispatch(new IncrementAction());  

// Assert the number of times IncrementAction() is dispatched.
expect(count).toBe(2);
```  

<br></br>
**Recording action values**

```ts
let values = [];

store.mocks.add(AddAction, (action) => {
  values.push(action.value);
  return action;
});
                       
store.dispatch(new AddAction(2));
store.dispatch(new AddAction(9));
store.dispatch(new AddAction(5));

// Assert the values of all AddAction's dispatched.
expect(values).toEqual([2, 9, 5]);     
```

<br></br>
**Recording dispatched action types**

```ts
let types = [];

store.mocks.add('*', (action) => {
    types.push(action.constructor.name);
    return action;
});

store.dispatch(new AddAction(2));
store.dispatch(new IncrementAction(9));
store.dispatch(new LoadUserAction());

// Assert the action types dispatched.
expect(types).toEqual([
  'AddAction', 
  'IncrementAction', 
  'LoadUserAction'
]);
```

## mocks.remove

Removes the mock for the given action type, from the store.
If the action type is not mocked, the removal will be ignored (will not throw an error).

```ts 
// Removes the mock for the IncrementAction.
store.mocks.remove(IncrementAction);
```

## mocks.clear

Removes all mocks, if any.

```ts 
store.mocks.clear();
```

## Testing The App

# Dispatch, wait and expect

Kiss's test capabilities are one of its strongest points,
making it very easy to test your app,
including both testing synchronous and asynchronous processes.

## Testing steps

Testing an Kiss app generally involves these steps, in order:

1. Set up the store and some initial state.
2. Dispatch one or more actions.
3. Wait for the actions to complete their dispatch, or for the store state to meet
   a certain condition.
4. Verify the current state, or the action status.

Item 3 above (waiting for actions to complete) can be done using the following functions:

* [dispatchAndWait](../basics/dispatching-actions#dispatch-and-wait)
* [dispatchAndWaitAll](../basics/dispatching-actions#dispatch-and-wait-all)

Click on the links above to see their documentation.

Example:

```ts
// Start with some IBM stocks
var store = Store<State>(initialState: State(portfolio: ['IBM']));

// Buy Tesla stocks  
await dispatchAndWait(new BuyAction('TSLA'));  

// Assert we now have IBM and Tesla
expect(store.state.portfolio).toEqual(['IBM', 'TSLA']);
```

## How about dispatch and dispatchAll?

Functions [**dispatch**](../basics/dispatching-actions#dispatch-one-action)
and [**dispatchAll**](../basics/dispatching-actions#dispatch-all-multiple-actions)
can also be used to dispatch actions in tests,
but they do **not** return a `Promise` that resolves when the action finishes.

When you dispatch a **synchronous** action, the action will finish **immediately**,
because that's how JavaScript works. But when you dispatch an **asynchronous** action,
the action will finish **later**, and you may need ways to wait for it to finish.

For this reason, it's probably simpler to always use `dispatchAndWait`
and `dispatchAndWaitAll` in tests.
They will always work no matter if actions are sync or async,
whether you want to wait for them to finish or not.

## Waiting for conditions

Besides the simple use cases above, where you dispatch actions directly and wait for them to finish,
the following functions can be used to wait for more complex conditions to be met:

* [store.waitCondition](../miscellaneous/wait-for-condition#waitcondition)
* [store.waitActionCondition](../miscellaneous/wait-for-condition#waitactioncondition)
* [store.waitAllActions](../miscellaneous/wait-for-condition#waitallactions)
* [store.waitActionType](../miscellaneous/wait-for-condition#waitallactiontypes-and-waitactiontype)
* [store.waitAllActionTypes](../miscellaneous/wait-for-condition#waitallactiontypes-and-waitactiontype)
* [store.waitAnyActionTypeFinishes](../miscellaneous/wait-for-condition#waitanyactiontypefinishes)

Click on the links above to see their documentation, with examples.

## Recording

It's possible to record all state changes, dispatched actions and errors thrown by actions.
Then your test checks if the recorded information is as expected.

* To start recording, call `store.record.start()`.

* To stop recording, call `store.record.stop()`.

* The recording itself is in `store.record.result` which contains an array of objects:

   ```ts
   [] as Array<{
     action: KissAction<St>,
     ini: boolean,
     prevState: St,
     newState: St,
     error: any,
     dispatchCount: number
   }>
   ```

* To get a text representation of the recording, call `store.record.toString()`.

For example, suppose we dispatch `SetNameAction` to put the name 'John' into the state.
Then `StartLoginAction` is dispatched to get this name and create a user from it.
Before it finishes, `StartLoginAction` dispatches `ResetNameAction` to reset the name to empty.
And finally, `StartLoginAction` finishes, adding the new user to the state.

This is how you could test this scenario using recording:

```text
it('should create user and reset name' async () => {

  const store = new Store<State>({ initialState: new State() });
  
  store.record.start();

  await store.dispatchAndWaitAll([
    new SetNameAction('John'),
    new StartLoginAction()
  ]);

  store.record.stop();

  let expected = `
  [
  1. SetNameAction ini(1): State(null, '')
  2. SetNameAction end: State(null, '') → State(null, 'John')
  3. StartLoginAction ini(2): State(null, 'John')
  4. ResetNameAction ini(3): State(null, 'John')
  5. ResetNameAction end: State(null, 'John') → State(null, '')
  6. StartLoginAction end: State(null, '') → State(User('John'), '')
  ]`;

  expect(store.record.toString()).toBe(expected);
});
```

While in theory you can always perform your tests by recording and checking the result,
that's not recommended for most cases, as those tests can be brittle and hard to maintain.

For example, the above actions could be better tested like this:

```text
it('should create user and reset name' async () => {

  const store = new Store<State>({ initialState: new State() });
  
  await store.dispatchAndWaitAll([
    new SetNameAction('John'),
    new StartLoginAction()
  ]);

  expect(store.state.user).toBe(User('John'));
  expect(store.state.name).toBe('');
});
```

It's recommended to use recording only where it's hard to test the end state directly.

## Testing User Exceptions

# Testing UserExceptions

As [previously discussed](../basics/user-exceptions), 
when an action encounters an error which is not a bug, but rather a user mistake, 
it can throw the built-in `UserException` error. 

This page discusses how to test when `UserException`s are thrown.

For example, suppose you want to test that users are warned 
if they typed letters in some field that only accepts numbers. 
To that end, your test would dispatch the appropriate action, wait for it
to finish, and then check the [action.status](../advanced-actions/action-status) field.

The status can tell us if the action finished with or without errors:

* `status.isCompleted` is `true` if the action finished, and `false` if the action is still running,
  or if it hasn't been dispatched yet.

* `status.isCompletedOk` is `true` if the action finished without errors (in more detail, if the
  action's methods `before` and `reduce` finished without throwing any errors).

* `status.isCompletedFailed` is equal to `!status.isCompletedOk`.

And then, there are two errors we can read:

* `status.originalError` is the error that was originally thrown by the action's `before`
  or `reduce` methods. However, this error might have been changed by the action itself, by the
  action's `wrapError()` method.

* `status.wrappedError` is the error that was thrown by the action's `before` or `reduce` methods,
  after being changed by the action itself, by the action's `wrapError()` method.
  If the action didn't change the error, `status.originalError` and `status.wrappedError` will be
  the same.

Since the `action.status` field is immutable, the whole field will be replaced during the action 
lifecycle. This means your test needs to wait until the action is finished before getting 
a copy of its status.

Here's an example:

```ts
let status = await store.dispatchAndWait(MyAction());
expect(status.isCompletedFailed).toBe(true);

let error = status.wrappedError; 
expect(error).toBeInstanceOf(UserException);
expect(error.msg).toBe("You can't do this.");
```

## Checking the error queue

Since `UserException`s don't represent bugs in the code, 
Kiss puts them into the store's `userExceptionsQueue` queue. 
In other words, this queue is a list of `UserException`s that were thrown by actions, 
and it will be consumed by the UI (usually a modal error dialog) to show the user.

If you test includes actions that emit a lot of `UserException` errors,
you may wait until they all enter the error queue, and then check the queue itself:

```ts
let status = await store.dispatchAndWaitAll([
  new MyAction1(), 
  new MyAction2(), 
  new MyAction3()
]);

let errors = store.userExceptionsQueue;

expect(errors.length).toBe(3);
expect(errors[0].msg).toBe("You can't do this.");
expect(errors[1].msg).toBe("You can't do that.");
expect(errors[2].msg).toBe("You can't do the other thing.");
```

---

# Miscellaneous

## Business Logic

Where should you put your business logic in your application architecture?

### ✓ State classes

State classes form the core of your business logic. For example, in a _Todo List_
application you can have a `TodoList` class that manages the list of todos,
and a `Todo` class that manages a single todo.
The recommendation is to implement the majority of your business logic within state classes.

### ✗ Actions and reducers

Actions and Reducers are also classified as business code.
But you should use them primarily only to invoke business logic from your state classes.

### ✗ React components

React Components are strictly client-side code.
Avoid implementing business logic in components.

### ✗ Custom react hooks

You should avoid creating and using custom hooks, when using Kiss,
as they are mostly not necessary. However, if you do use custom hooks,
avoid incorporating business logic in them.

## Example

To better understand this architecture, let's review the _Todo List_ application
created in the [Tutorial](../tutorial/full-code) section, and find
where is the code that implements **toggling a todo item as active or completed**.

Here is the code of the todo item **component**:

```tsx
function TodoItemComponent({ item }: { item: TodoItem }) {

  const store = useStore();
  const filter = useSelect((state: State) => state.filter);

  return item.ifShows(filter) ? (
    <label>
      <input
        type="checkbox"
        checked={item.completed}
        onChange={() => store.dispatch(new ToggleTodoAction(item))}
      />
      <div>{item.text}</div>
    </label>
  ) : (
    <span />
  );
}
```

As you can see above, the component code does not contain any code to toggle a todo item. 
Instead, it dispatches an action called `ToggleTodoAction`.
So let's see the code of this **action** and its **reducer**:

```tsx
class ToggleTodoAction extends Action {
  constructor(readonly item: TodoItem) { super(); }

  reduce() {
    let newTodoList = this.state.todoList.toggleTodo(this.item);
    return this.state.withTodoList(newTodoList);
  }
}
```

Again, as you can see above, the action also does not contain any code to toggle a todo item.
Instead, it calls the `toggleTodo()` function of the `todoList` object present in the **state**.
So let's see the code of this function in the `TodoList` class:

```tsx
export class TodoList {
  constructor(public readonly items: TodoItem[] = []) {}  

  toggleTodo(item: TodoItem): TodoList {
    const newTodos = this.items.map((itemInList) =>
      itemInList === item ? item.toggleCompleted() : itemInList
    );
    return new TodoList(newTodos);
  }

  ...
}
```

Finally, here is the code that actually toggles the todo item.
It iterates over the todo items and toggles the one that matches the item we want.

## Log And Metrics

When you create your store, you can optionally pass it an `actionObservers` and
a `stateObserver`, which may be used for logging and collecting metrics for your app:

```tsx
const store = createStore<State>({
  initialState: State.initialState,
  actionObserver: actionObserver, // Here!
  stateObserver: stateObserver, // Here!
});
```

## actionObserver

The `actionObserver` is a function which is notified of any **action dispatching**.

Its parameters are:

- `action` is the action itself.

- `dispatchCount` is the sequential number of the dispatch.

- `ini` is the observer will actually be called twice for each action dispatch:
  One with `ini: true` when the action is dispatched (before the state is changed),
  and one with `ini: false` when the action finished dispatching (after the state is changed).

The action-observer is a good place to log which actions are dispatched by your application.
For example, the following code logs actions to the console in development and test modes,
but saves metrics in production mode:

```ts
function actionObserver(action, dispatchCount, ini) {
  if (inDevelopment() || inTests()) {
     if (ini) console.log('Action dispatched: ${action}');
     else console.log('Action finished: ${action}');
  } else {
    if (ini) saveMetrics('Dispatched: ${action}');
  }
}
```

Note the `saveMetrics` function above is a placeholder for your actual metrics saving function.

## stateObserver

The `stateObserver` is a function called for all dispatched actions,
right after the reducer returns, before the action's `after()` method,
before the action's `wrapError()`, and before the `globalWrapError()`.

Its parameters are:

- `action` is the action that changed the state.

- `prevState` is the state right before the new state returned by the reducer is applied. Note
  this may be different from the state when the action was dispatched.

- `newState` is the state returned by the reducer. Note: If you need to know if the state was
  changed or not by the reducer, you can compare both
  states: `let ifStateChanged = (prevState !== newState);`

- `error` is null if the action completed with no error. Otherwise, will be the error thrown by
  the reducer (before any wrapError is applied). Note that, in case of error, both `prevState`
  and `newState` will be the current store state when the error was thrown.

- `dispatchCount` is the sequential number of the dispatch.

The state-observer is a good place to log actions and state, and collect metrics. For example:

```ts
function stateObserver(action, prevState, newState, error, dispatchCount) {
  saveMetrics(action, newState, error);
}
```

If we want to improve the saved metrics, our actions have a `log()` function that
can be used to log extra information. For example:

```ts
class LoadUser extends Action {  
  
  async reduce() {
    let user = await loadUser();
    this.log('User', user.id); // Here!
    
    return (state) => state.copy({user: user});   
  }    
}
```        

And then, the state observer can read and use the action log:

```ts
function stateObserver(action, prevState, newState, error, dispatchCount) {
  let actionLog = action.getLog(); // Here!
  saveMetrics(action, actionLog, newState, error);
}
```

## Try it yourself

**[Live Example](https://codesandbox.io/s/3rzvsk)**

## Persistor

The **persistor** allows you to save the store state to the local device disk.

- In the **web**, it allows the user to reload the page,
  or close the browser and reopen it later, without losing the previous state.

- In **React Native**, it allows the user to kill the app and reopen it later,
  without losing the previous state.

## Setup

You must set up your persistor during the store creation:

```tsx          
const store = createStore<State>({  
  initialState: ...  
  persistor: persistor, // Here!
});        
```

Let's first see how to implement your own persistor,
and then let's see how to use the `ClassPersistor` that comes out of the box with Kiss.

## Implementation

All a persistor needs to do is to implement the abstract `Persistor` interface.
This interface is shown below, with its four functions that must be
implemented: `readState`, `deleteState`, `persistDifference` and `saveInitialState`.

Read the comments in the code below to understand what each function should do.

```tsx
export abstract class Persistor<St> {
 
  // Function `readState` should read/load the saved state from the 
  // persistence. It will be called only once per run, when the app  
  // starts, during the store creation.
  //
  // - If the state is not yet saved (first app run), `readState`  
  //   should return `null`.
  //  
  // - If the saved state is valid, `readState` should return the 
  //   saved state.
  //
  // - If the saved state is corrupted but can be fixed, `readState`   
  //   should save the fixed state and then return it.
  //
  // - If the saved state is corrupted and cannot be fixed, or some  
  //   other serious error occurs while reading the state, `readState`   
  //   should thrown an error, with an appropriate error message.
  //
  // Note: If an error is thrown by `readState`, Kiss will log  
  // it with `Store.log()`. 
  abstract readState(): Promise<St | null>;

  // Function `deleteState` should delete/remove the saved state from 
  // the persistence.    
  abstract deleteState(): Promise<void>;

  // Function `persistDifference` should save the new state to the 
  // persistence, and return a `Promise` that completes only after 
  // it is persisted.
  //
  // This new state is provided to the function as a parameter 
  // called `newState`. For simpler apps where your state is small, 
  // you can simply persist the whole `newState` every time. 
  //
  // But for larger apps, you may compare it with the last persisted state, 
  // and persist only the difference between them. The last persisted state 
  // is provided to the function as a parameter called `lastPersistedState`. 
  // It may be `null` if there is no persisted state yet (first app run).  
  abstract persistDifference(
    lastPersistedState: St | null,
    newState: St
  ): Promise<void>;

  // Function `saveInitialState` should save the given `state` to the 
  // persistence, replacing any previous state that was saved.  
  abstract saveInitialState(state: St): Promise<void>;

  // The default throttle is 2 seconds (2000 milliseconds). 
  // Return `null` to turn off the throttle.   
  get throttle(): number | null {
    return 2000; 
  }
}
```

Kiss will call these functions at the right time, so you don't need to worry about it:

* When the app opens, Kiss will call `readState()` to get the last state that was persisted.

* In case there is no persisted, state yet (first time the app is opened), the `saveInitialState()`
  function will be called to persist the initial state.

* In case there is a persisted state, but it's corrupted (reading the state fails with an error),
  then `deleteState()` will be called first to delete the corrupted state,
  and then `saveInitialState()` will be called to persist the initial state.

* In case the persisted state read with `readState()` is valid, this will become the current store
  state.

* From this moment on, every time the state changes, Kiss will schedule a call to
  the `persistDifference()` function. This function will not be called more than once each 2
  seconds, which is the default throttle interval. You can change it by overriding the `throttle`
  property (make it zero if you want no throttle, and the state will save as soon as it changes).

* In the unlikely case the `persistDifference()` function itself takes more than 2 seconds to
  execute, the next call will be scheduled only after the current one finishes.

* The `persistDifference()` function receives the last persisted state and the current new state.
  The simplest way to implement this function is to ignore the `lastPersistedState` parameter,
  and persist the whole `newState` every time. This is fine for small states, but for larger
  states you can compare the two states and persist only the difference between them.

* Even if you have a non-zero throttle period, sometimes you may want to save the state immediately,
  for some reason. You can do that by dispatching the built-in `PersistAction`
  with `dispatch(new PersistAction());`. This will ignore the throttle period and
  call `persistDifference()` right away to save the current state.

## ClassPersistor

Kiss comes out of the box with the `ClassPersistor` that implements the `Persistor`
interface. It supports serializing ES6 classes out of the box,
and it will persist the whole state of your application.

To use it, you must provide these function:

* `loadSerialized`: a function that returns the serialized state.
* `saveSerialized`: a function that saves the serialized state.
* `deleteSerialized`: a function that deletes the serialized state.
* `classesToSerialize`: an array of all the _custom_ classes that are part of your state.

In more detail, here's the `ClassPersistor` constructor signature:

```tsx
constructor(

  // Returns the serialized state.
  // It should return a Promise that resolves to the saved serialized 
  // state, or to null if the state is not yet persisted.
  public loadSerialized: () => Promise<string | null>,
    
  // Saves the given serialized state. 
  // It should return a Promise that resolves when the state is saved.    
  public saveSerialized: (serialized: string) => Promise<void>,
    
  // Deletes the serialized state. 
  // It should return a Promise that resolves when the state is deleted.
  public deleteSerialized: () => Promise<void>,
    
  // List here all the custom classes that are part of your state, directly 
  // or indirectly. Note: You don't need to list native JavaScript classes. 
  public classesToSerialize: Array<ClassOrEnum>
)
```

<br></br>

Here is the simplest possible persistor declaration that uses the `ClassPersistor`.
It uses `window.localStorage` for React web, and `AsyncStorage` for React Native:

```tsx 
let persistor = new ClassPersistor<State>(

  // loadSerialized
  async () => window.localStorage.getItem('state'),
  
  // saveSerialized
  async (serialized: string) => window.localStorage.setItem('state', serialized),
  
  // deleteSerialized
  async () => window.localStorage.clear(),
  
  // classesToSerialize
  []
);
```

```tsx
let persistor = new ClassPersistor<State>(

  // loadSerialized
  async () => await AsyncStorage.getItem('state'),
  
  // saveSerialized
  async (serialized) => await AsyncStorage.setItem('state', serialized),
  
  // deleteSerialized
  async () => await AsyncStorage.clear(),
  
  // classesToSerialize
  [] 
);
```

As explained, the `ClassPersistor` supports serializing ES6 classes.
However, you will need to list all class types in the `classesToSerialize` parameter above.

For example, consider the _Todo List_ app shown below,
which was created in our [tutorial](../category/tutorial).
It uses classes called `State`, `TodoList`, `TodoItem`, and `Filter` in its state.
This means that you must list them all in the `classesToSerialize` parameter of
the `ClassPersistor`:

```tsx
// classesToSerialize
[State, TodoList, TodoItem, Filter]
```

To see the persistence in action,
try adding some items to the todo list below, and then reload the page.
You should see those items surviving the reload.

**[Live Example](https://codesandbox.io/s/sw3g2t)**

## App lifecycle

In mobile apps, you have to understand the app lifecycle to use the persistor correctly:

* Foreground: The app is active and running, and is visible to the user.
* Background: The app is running but is not visible to the user, usually because the user has
  switched to another app or returned to the home screen.
* Inactive: The app is transitioning between states, such as when an incoming call occurs, but the
  user has not yet decided whether to accept or reject the call.
* Terminated: The app was killed, and is not running. It can be explicitly terminated by the user
  or the system.

When the app goes to the **background**, you may want to call `store.pausePersistor()`
to **pause** the persistor, and then **resume** it by calling `store.resumePersistor()`
when the app comes back to the **foreground** .

However, when the app is **terminated**, it's a different story.
In this case, you must force the persistor to save the state immediately.
This is necessary because a throttle of a few seconds was probably defined for the persistor.
For example, suppose the throttle is 2 seconds (the default),
but the app is killed 1 second after the last save.

In this case, all state changes for the last second will be lost.
To avoid this, as soon as you detect that the app is about to be killed,
you should call `store.persistAndPausePersistor()` to save the state immediately,
and then pause the persistor.

## Log out

When your user logs out of your app, or deletes its user account,
you want to go back to the login page, and allow another user to log in,
or start a new sign-up process.

To that end, you need to delete the persisted state, and return the store
state to its initial-state.

You may be temped to write `dispatch(new UpdateStateAction((state: State) => initialState));`
but that's not so simple. The persistor may be waiting for the throttle period, some async
actions may still be running, etc. Thankfully, Kiss provides you with a `store.signOut()`
function that you can call to perform this process safely.

This is how you can do it:

```ts
await store.logOut({
  initialState: State.initialState,
  throttle = 3000,
  actionsThrottle = 6000,
})
```  

When this function returns, your initial store state will be restored to its initial state.

Defining `throttle` and `actionsThrottle` above is optional, because
the default `throttle` is 3 seconds, and the default `actionsThrottle` is 6 seconds.
This is how `signOut()` uses them:

- Waits for `throttle` milliseconds to make sure all async processes that the app may
  have started have time to finish.

- Waits for all actions currently running to finish, but wait at most `actionsThrottle`
  milliseconds. If the actions are not finished by then, the state will be deleted anyway.

:::warning

If you know about any timers or async processes that you may have started, you should stop/cancel
them all **before** calling the `logOut()` function.

Also, it's up to you to redirect the user to the login page after `logOut()` returns.

:::

## Manually accessing the persistor

The functions below are probably only useful for **testing** the persistence of your app.
Only use them in production if you know exactly what you're doing,
and you have a very good reason to do so.

As explained above, when you create the persistor you add it to the store:

```tsx          
const store = createStore<State>({  
  initialState: ...  
  persistor: persistor, 
});        
```

After this, you should **not** keep a reference to the persistor,
and should not call any of the persistor functions.

Since Kiss is managing the persistor, calling the persistor functions directly
may disrupt the delicate process of keeping track of state changes.

However, you can still use the persistor **indirectly** through the store:

* `saveInitialStateInPersistence(initialState)` asks the Persistor to save the
  given `initialState` to the local device disk.

* `readStateFromPersistence` asks the Persistor to read the state from the local device disk.
  If you use this function, you **must** yourself put this state into the store.
  Kiss will assume that's the case, and will not work properly otherwise.

* `deleteStateFromPersistence()` asks the Persistor to delete the saved state from the local
  device disk.

* `getLastPersistedStateFromPersistor()` gets the last state that was saved by the Persistor.

## Slices

If you are used to Redux Toolkit or Zustand,
you may be familiar with the concept of _slices_.

The idea behind it is that your store can become bigger and bigger,
and tougher to maintain as you add more features.
With slices, you can treat your store as separate smaller individual stores.

## How to

I will show you how to implement slices with Kiss,
and then I'm going to tell you why I think they are **not necessary**.

Consider the state below, for a stock trading application:

```tsx
state
├── user
│   ├── id: string
│   ├── name: string
│   ├── email: string
│   ├── isAuthenticated: boolean
│   ├── preferences
│   │   ├── theme: string
│   │   ├── notifications: boolean
│   │   └── language: string
│   ├── portfolio
│   │   ├── totalValue: number
│   │   ├── stocks
│   │   │   ├── [stockId]
│   │   │   │   ├── ticker: string
│   │   │   │   ├── name: string
│   │   │   │   ├── quantity: number
│   │   │   │   ├── averageCost: number
│   │   │   │   └── currentValue: number
│   │   │   ├── [stockId]
│   │   │   │   ├── ...
│   │   │   └── ...
│   │   └── cashBalance: number
│   └── watchlist
│       ├── [stockId]
│       │   ├── ticker: string
│       │   ├── name: string
│       │   └── targetPrice: number
│       ├── [stockId]
│       │   ├── ...
│       └── ...
├── stocks
│   ├── [stockId]
│   │   ├── ticker: string
│   │   ├── name: string
│   │   ├── currentPrice: number
│   │   ├── dailyChange: number
│   │   ├── dailyChangePercent: number
│   │   ├── volume: number
│   │   └── marketCap: number
│   ├── [stockId]
│   │   ├── ...
│   └── ...
├── transactions
│   ├── [transactionId]
│   │   ├── stockId: string
│   │   ├── type: string
│   │   ├── quantity: number
│   │   ├── price: number
│   │   ├── date: string
│   │   └── status: string
│   ├── [transactionId]
│   │   ├── ...
│   └── ...
├── news
│   ├── [newsId]
│   │   ├── title: string
│   │   ├── description: string
│   │   ├── url: string
│   │   ├── source: string
│   │   └── date: string
│   ├── [newsId]
│   │   ├── ...
│   └── ...
├── alerts
│   ├── [alertId]
│   │   ├── stockId: string
│   │   ├── type: string
│   │   ├── targetPrice: number
│   │   ├── message: string
│   │   ├── isActive: boolean
│   │   └── date: string
│   ├── [alertId]
│   │   ├── ...
│   └── ...
└── settings
    ├── theme: string
    ├── notifications: boolean
    ├── language: string
    └── security
        ├── twoFactorAuth: boolean
        ├── backupEmail: string
        └── changePasswordDate: string
```

```ts
class Stock {
    constructor(
        public ticker: string,
        public name: string,
        public currentPrice: number,
        public dailyChange: number,
        public dailyChangePercent: number,
        public volume: number,
        public marketCap: number
    ) {}
}

class PortfolioStock {
    constructor(
        public ticker: string,
        public name: string,
        public quantity: number,
        public averageCost: number,
        public currentValue: number
    ) {}
}

class WatchlistStock {
    constructor(
        public ticker: string,
        public name: string,
        public targetPrice: number
    ) {}
}

class Portfolio {
    constructor(
        public totalValue: number,
        public stocks: { [stockId: string]: PortfolioStock },
        public cashBalance: number
    ) {}
}

class Preferences {
    constructor(
        public theme: string,
        public notifications: boolean,
        public language: string
    ) {}
}

class User {
    constructor(
        public id: string,
        public name: string,
        public email: string,
        public isAuthenticated: boolean,
        public preferences: Preferences,
        public portfolio: Portfolio,
        public watchlist: { [stockId: string]: WatchlistStock }
    ) {}
}

class Transaction {
    constructor(
        public stockId: string,
        public type: string,
        public quantity: number,
        public price: number,
        public date: string,
        public status: string
    ) {}
}

class News {
    constructor(
        public title: string,
        public description: string,
        public url: string,
        public source: string,
        public date: string
    ) {}
}

class Alert {
    constructor(
        public stockId: string,
        public type: string,
        public targetPrice: number,
        public message: string,
        public isActive: boolean,
        public date: string
    ) {}
}

class Security {
    constructor(
        public twoFactorAuth: boolean,
        public backupEmail: string,
        public changePasswordDate: string
    ) {}
}

class Settings {
    constructor(
        public theme: string,
        public notifications: boolean,
        public language: string,
        public security: Security
    ) {}
}

class State {
    constructor(
        public user: User,
        public stocks: { [stockId: string]: Stock },
        public transactions: { [transactionId: string]: Transaction },
        public news: { [newsId: string]: News },
        public alerts: { [alertId: string]: Alert },
        public settings: Settings
    ) {}
}
```

```ts
interface Stock {
    ticker: string;
    name: string;
    currentPrice: number;
    dailyChange: number;
    dailyChangePercent: number;
    volume: number;
    marketCap: number;
}

interface PortfolioStock {
    ticker: string;
    name: string;
    quantity: number;
    averageCost: number;
    currentValue: number;
}

interface WatchlistStock {
    ticker: string;
    name: string;
    targetPrice: number;
}

interface Portfolio {
    totalValue: number;
    stocks: { [stockId: string]: PortfolioStock };
    cashBalance: number;
}

interface Preferences {
    theme: string;
    notifications: boolean;
    language: string;
}

interface User {
    id: string;
    name: string;
    email: string;
    isAuthenticated: boolean;
    preferences: Preferences;
    portfolio: Portfolio;
    watchlist: { [stockId: string]: WatchlistStock };
}

interface Transaction {
    stockId: string;
    type: string;
    quantity: number;
    price: number;
    date: string;
    status: string;
}

interface News {
    title: string;
    description: string;
    url: string;
    source: string;
    date: string;
}

interface Alert {
    stockId: string;
    type: string;
    targetPrice: number;
    message: string;
    isActive: boolean;
    date: string;
}

interface Security {
    twoFactorAuth: boolean;
    backupEmail: string;
    changePasswordDate: string;
}

interface Settings {
    theme: string;
    notifications: boolean;
    language: string;
    security: Security;
}

interface State {
    user: User;
    stocks: { [stockId: string]: Stock };
    transactions: { [transactionId: string]: Transaction };
    news: { [newsId: string]: News };
    alerts: { [alertId: string]: Alert };
    settings: Settings;
}   
```

As [previously discussed](../advanced-actions/base-action-with-common-logic),
you may create a base action class called `Action` that extends `KissAction`.
By default, all actions that on their turn extend `Action` have access to the entire state,
and can change any part of it.

To implement slices, you can implement other extra base actions,
each with access to only a part of the state.

The first level of the state shown above
contains `user`, `stocks`, `transactions`, `news`, `alerts`, and `settings`.
Each of these could be a slice, so let's create a base action
that has easier access to the `user` state, and can only change that `user` state:

Here is the code for when your state is made of classes,
and also for when it's made of plain objects:

```tsx
abstract class UserAction extends Action {

  // Getter shortcut to the user state.
  get user(): User { return this.state.user; }
  
  // User specific reducer.
  abstract reduceSlice(): User | null | Promise<((user: User) => (User | null)) | null>;

  // Override the reduce method to call reduceSlice.
  reduce() {
    let result = this.reduceSlice(this.state.user);
    if (result === null) return null;    
    else if (result instanceof Promise) {
      return result.then((promiseReducer) => {
        if (promiseReducer === null) return null;        
        return (state: State) => {
          let newData = promiseReducer(state.user);
          if (newData === null) return null;          
          return state.withUser(newData);
        };
      });
    }       
    else return state.withUser(newData);
  }
}
```

```tsx
abstract class UserAction extends Action {

  // Getter shortcut to the user state.
  get user(): User { return this.state.user; }
  
  // User specific reducer.
  abstract reduceSlice(): User | null | Promise<((user: User) => (User | null)) | null>;

  // Override the reduce method to call reduceSlice.
  reduce() {
    let result = this.reduceSlice(this.state.user);
    if (result === null) return null;    
    else if (result instanceof Promise) {
      return result.then((promiseReducer) => {
        if (promiseReducer === null) return null;        
        return (state: State) => {
          let newData = promiseReducer(state.user);
          if (newData === null) return null;          
          return { ...state, user: newData };
        };
      });
    }       
    else return { ...state, user: newData };
  }
}
```

The code above is a bit complex, but you don't need to understand it much.
Simply copy it and adapt it to your needs.
Also, you only need to write it once and then forget about it.

<br></br>

To use it, you should write `extends UserAction`:

```tsx
class DuplicatePortfolio extends UserAction { 
  
  reduceSlice() {
    // Type `this.user` instead of `this.state.user`
    let newPortfolio = this.user.portfolio.duplicate();    
    
    // Return a `User` object instead of a `State`.
    return this.user.withPortfolio(newPortfolio); 
  }   
}
```

You may have noted that slices in Kiss are simply "views"
where your actions only see and change part of the state.
This means you can slice the same state as many times as you see fit,
in overlapping ways, and you are not restricted to slicing the first level of the state.

For example, let's see how to create a slice for the second level, `user.portfolio`:

```tsx
abstract class PortfolioAction extends Action {

  // Getter shortcut to the portfolio state.
  get portfolio(): Portfolio { return this.state.user.portfolio; }
  
  // Portfolio specific reducer.
  abstract reduceSlice(): Portfolio | null | Promise<((portfolio: Portfolio) => (Portfolio | null)) | null>;

  // Override the reduce method to call reduceSlice.
  reduce() {
    let result = this.reduceSlice(this.state.user.portfolio);
    if (result === null) return null;    
    else if (result instanceof Promise) {
      return result.then((promiseReducer) => {
        if (promiseReducer === null) return null;        
        return (state: State) => {
          let newData = promiseReducer(state.user.portfolio));
          if (newData === null) return null;          
          return state.withUser(state.user.withPortfolio(newData));
        };
      });
    }       
    else return state.withUser(state.user.withPortfolio(newData));
  }
}
```

```tsx
abstract class PortfolioAction extends Action {

  // Getter shortcut to the portfolio state.
  get portfolio(): Portfolio { return this.state.user.portfolio; }
  
  // Portfolio specific reducer.
  abstract reduceSlice(): Portfolio | null | Promise<((portfolio: Portfolio) => (Portfolio | null)) | null>;

  // Override the reduce method to call reduceSlice.
  reduce() {
    let result = this.reduceSlice(this.state.user.portfolio);
    if (result === null) return null;    
    else if (result instanceof Promise) {
      return result.then((promiseReducer) => {
        if (promiseReducer === null) return null;        
        return (state: State) => {
          let newData = promiseReducer(state.user.portfolio));
          if (newData === null) return null;          
          return {...state, user: { ...state.user, portfolio: newData } };
        };
      });
    }       
    else return {...state, user: { ...state.user, portfolio: newData } };
  }
}
```

<br></br>

To use it, you should now write `extends PortfolioAction`:

```tsx
class DuplicatePortfolio extends PortfolioAction { 
  
  reduceSlice() {
    // Type `this.portfolio` instead of `this.state.user.portfolio`
    let newPortfolio = this.portfolio.duplicate();
    
    // Return a `Portfolio` object instead of a `State`.
    return newPortfolio; 
  }   
}
```

## Why slices may not be necessary

As explained, the idea behind slices is that your store
can become tougher to maintain as you add more features.

However, with Kiss, your state naturally stays simple enough
that it won't get tougher to maintain.
That's specially true if your state is made
of [classes](../tutorial/creating-the-state)
or [objects with functions](../tutorial/plain-javascript-obj#state-as-objects-with-functions).

However, if your state is made
of simple [value objects](../tutorial/plain-javascript-obj#state-as-value-objects),
then it's possible things get more complex with time,
and at some point you'll want to implement slices.

## Easy state access without slices

Even if you create no slices, you can still
add shortcut getters and selectors to your base `Action`,
as [previously discussed](../advanced-actions/base-action-with-common-logic).

For example:

```tsx
import { KissAction } from 'kiss-for-react';
import { State } from 'State';

export abstract class Action extends KissAction<State> { 

  // Getter shortcut to the user state.
  get user(): User { return this.state.user; }
  
  // Getter shortcut to the portfolio state.
  get portfolio(): Portfolio { return this.state.user.portfolio; }
  
  // Selector to get a stock by ID from the user's portfolio.
  getStockById(stockId: string): Stock | undefined {
    return this.portfolio.stocks[stockId];
  }
}
```

This makes it easier to access the state properties in your actions,
which is half the reason you would want to create slices anyway.

In this case, however, you still need to return complete `State` objects.
For example:

```tsx
class DuplicatePortfolio extends Action { 
  
  reduceSlice() {
    // Type `this.portfolio` instead of `this.state.user.portfolio`
    let newPortfolio = this.portfolio.duplicate();    
    
    // Type `this.user` instead of `this.state.user`
    let newUser = this.user.withPortfolio(newPortfolio)
    
    // You need to return a `State` object.
    return this.state.withUser(newUser);
    ); 
  }   
}
```

```tsx
class DuplicatePortfolio extends Action { 
  
  reduceSlice() {
    // Type `this.portfolio` instead of `this.state.user.portfolio`
    let newPortfolio = duplicatePortfolio(this.portfolio);    
    
    // Type `this.user` instead of `this.state.user`
    let newUser = { ...this.user, portfolio: newPortfolio };
    
    // You need to return a state object.
    return { ...this.state, user: newUser };
  }
  
  duplicatePortfolio(portfolio: Portfolio): Portfolio {
    // Implement the logic to duplicate the portfolio
  }  
}
```

<br></br>

## Undo And Redo

It's easy to create undo/redo features in Kiss. 

For example, here we create the store with 
a [state-observer](./log-and-metrics#stateobserver) that saves the most recent 100 states:

```dart
var store = Store<State>(
  initialState: state,  
  stateObserver: stateObserver,
);

const stateHistory = [];

function stateObserver(action, prevState, newState, error, dispatchCount) {
  stateHistory.push(newState);
  if (stateHistory.length > 100) {
    stateHistory.shift();
  }
}
```

When you want to recover one of the states, simple dispatch the built-in `UpdateStateAction`:

```dart
// Recover the 42nd state in the history
dispatch(new UpdateStateAction((state) => stateHistory[41]);
```

:::tip

This also works to undo/redo only part of the state. If you are only interested in undoing part
of the state, your observer can save only that part, and your action can revert only that part.

:::

## Try it yourself
              
The app below shows three counters, and three `+` buttons to change them.
Press the `Undo` and `Redo` buttons to undo and redo changes.
The current and past states are shown in the `History` section.
Undone states are shown in grey.

**[Live Example](https://codesandbox.io/s/njfpht)**

## Wait For Condition

Kiss comes with a few functions to help you wait until some condition is met.
You can create conditions that wait until the store state is in a certain way,
or until actions in progress are in a certain way.

* [waitCondition](#waitcondition) waits until the state is in a condition.
* [waitAllActions](#waitallactions) waits until actions are not in progress.
* [waitAllActionTypes](#waitallactiontypes-and-waitactiontype) waits until all action types are NOT
  in progress.
* [waitActionType](#waitallactiontypes-and-waitactiontype) waits until an action of a type is not in
  progress.
* [waitAnyActionTypeFinishes](#waitanyactiontypefinishes) waits until action types finish
  dispatching.
* [waitActionCondition](#waitactioncondition) waits until actions in progress meet a condition.

## waitCondition

You can use the `waitCondition` function to wait until the app state changes in a certain way.
In more detail, you get a promise which will resolve when a given state condition is true.

For example, suppose your state contains a `stocks` object that allows you to get the
stock price for a given stock:

```tsx
let price = state.stocks.getPrice('IBM');
```

Now suppose you want to wait until the current price of IBM is 100 or more.
This is how you can do it:

```tsx
await waitCondition(
  (state) => state.stocks.getPrice('IBM') >= 100
);
```

In actions, you can use `this.waitCondition`. For example:

```tsx
class SellStockForPrice extends Action {
  constructor(public stock: string, public price: number) { super(); }

  async reduce() {
  
    // Wait until the stock price is higher than the limit price
    await this.waitCondition(
      (state) => state.stocks.getPrice(this.stock) >= this.price
    );
    
    // Only then, post the sell order to the backend
    let amount = await postSellOrder(this.stock);    
    
    return (state) => 
      state.copy({
        stocks: state.stocks.setAmount(this.stock, amount)
      });
  }
}
```

Keep in mind you should probably avoid waiting for conditions that may take a very long time to
complete, as checking the condition is an overhead to every state change.

:::info

If the condition is already true when the `waitCondition` function is called,
the promise resolves immediately.

:::

### dispatchWhen

The special dispatch function `dispatchWhen` allows you to
wait until the store state meets a certain condition, and then dispatch an action.
For example, this will dispatch a `BuyStock` action when the price of IBM is 100 or more:

```ts
dispatchWhen(
  new BuyStock('IBM'),
  (state) => state.stocks.getPrice('IBM') >= 100,
);
```

Note this dispatch function is just a shorthand for:

```ts
waitCondition(condition).then(() => this.dispatch(action));
```

### In tests

The `waitCondition` function is also very useful in tests.
You can dispatch actions that perform some complex stuff,
and then simply wait until the state reaches the exact condition you want to test for.

In the following example, we dispatch a `LogInUser` action 
and then wait until the user is logged in:

```ts
const store = new Store<State>({ initialState: new State() });
expect(store.state.user.isLoggedIn).toBe(false);

dispatch(new LogInUser("Mary"));
await store.waitCondition((state) => state.user.isLoggedIn);
expect(store.state.user.name, "Mary");
```

Another useful fact is that the `waitCondition` function
returns you the exact action (called the "trigger action")
that changed the state to meet the condition.
This can be helpful in tests when you need to assert
the specific action that caused the state change:

```ts
let action = await store.waitCondition(
  (state) => state.name == "Bill"
);
  
expect(action instanceof ChangeNameAction).toBe(true);
```

:::tip

What happens if the condition is never met, and your test never finishes?
To prevent that, you can set the `timeoutMillis` parameter.
For example, this would time out after 1 second (1000 milliseconds):

```ts
let action = await store.waitCondition(
  (state) => state.name == "Bill",
  1000,
);
```

The default timeout is 10 minutes, but you can
modify `TimeoutException.defaultTimeoutMillis` to change this default globally.

To disable the timeout completely, make it `0` or `-1`.

:::

## waitAllActions

You can use the `waitAllActions` function to wait until no actions are in progress:

```ts
// Initially we have some IBM stocks
expect(store.state.portfolio).toEqual(['IBM']);

// We'll sell IBM and buy TSLA, dispatching actions in parallel 
dispatch(new SellAction('IBM')); 
dispatch(new BuyAction('TSLA'));  

// We wait until ALL actions finish
await store.waitAllActions([]);

// Now we should have TSLA stocks and no IBM stocks
expect(store.state.portfolio).toEqual(['TSLA']);
```

:::info

In the code above, dispatching both actions in parallel could also have been done like this:

```
dispatchAll([
  new SellAction('IBM'),
  new BuyAction('TSLA')
]);
```

:::

Instead of waiting for all actions to finish, you can wait for **specific actions** to finish.
In this case, you'll need a reference to the actions you want to wait for, before dispatching them:

```ts
// Initially we have some IBM stocks
expect(store.state.portfolio).toEqual(['IBM']);

// We'll sell IBM and buy TSLA, dispatching actions in parallel 
var action1 = new SellAction('IBM');
var action2 = new BuyAction('TSLA');
dispatchAll([action1, action2]);

// We wait until both actions finish
await store.waitAllActions([action1, action2]);

// Now we should have TSLA stocks and no IBM stocks
expect(store.state.portfolio).toEqual(['TSLA']);
```

Note, in this case we could have used `dispatchAndWaitAll` instead:

```ts
// Initially we have some IBM stocks
expect(store.state.portfolio).toEqual(['IBM']);

// We'll sell IBM and buy TSLA, dispatching actions in parallel,
// and then wait until both actions finish 
await dispatchAndWaitAll([
  new SellAction('IBM'), 
  new BuyAction('TSLA')
]);

// Now we should have TSLA stocks and no IBM stocks
expect(store.state.portfolio).toEqual(['TSLA']);
```

Or, if we don't mind that the actions run in **series**
(one after the other, instead of running in parallel),
we could have used `dispatchAndWait`:

```ts
// Initially we have some IBM stocks
expect(store.state.portfolio).toEqual(['IBM']);

// We'll sell our IBM and buy TSLA, separately, and wait for each  
await dispatchAndWait(new SellAction('IBM')); 
await dispatchAndWait(new BuyAction('TSLA'));  

// Now we should have TSLA stocks and no IBM stocks
expect(store.state.portfolio).toEqual(['TSLA']);
```

:::warning

Only in **tests** you should use `waitAllActions([])` to wait until **no actions** are in progress.
Do not use it in production, because it can easily cause a deadlock. In tests it's acceptable,
because the test will fail and you can set a timeout.

However, you can use something like `waitAllActions([myAction])` in production,
to wait for **specific actions** to finish.
That's safe in production, as long as you're waiting for actions you just dispatched.

:::

When the promise resolves, you get back the set of actions being dispatched that met the condition,
as well as the action that triggered the condition by being added or removed from the set.

## waitAllActionTypes and waitActionType

You can use the `waitAllActionTypes` function to wait until **all** actions of the given **type**
are not in progress:

- If **no** action of the given types is currently in progress when the function is called,
  and parameter `completeImmediately` is `false` (the default), this function will throw an error.

- If **no** action of the given type is currently in progress when the function is called, and
  parameter `completeImmediately` is `true`, the promise completes immediately and throws no error.

- If **any** action of the given types is in progress, the promise completes only when
  no action of the given types is in progress anymore.

For example:

```ts
// Initially we have some IBM stocks
expect(store.state.portfolio).toEqual(['IBM']);

// We'll sell our IBM and buy TSLA
dispatch(new SellAction('IBM'));
dispatch(new BuyAction('TSLA'));

// We wait until the above action types finish
await store.waitAllActionTypes([BuyAction, SellAction]);

// Now we should have TSLA stocks and no IBM stocks
expect(store.state.portfolio).toEqual(['TSLA']);
```

The `waitActionType` function is very similar to the above `waitAllActionTypes`,
but it waits for a single action type.
The important difference is that it returns the action that caused the condition to be met.
You can use this returned action to check its `status`, for example, to assert it failed:

```ts
var action = await store.waitActionType(MyAction);
expect(action.status.isCompleteOk).toBe(false);
expect(action.status.isCompleteFailed).toBe(true);
expect(action.status.originalError, isA<UserException>());
```

## waitAnyActionTypeFinishes

You can use the `waitAnyActionTypeFinishes` function to wait until **any** action of the given
types **finishes dispatching**.

This function is different from the other similar functions above, because
it does NOT complete immediately if no action of the given types is in progress. Instead,
it waits until an action of the given types finishes
dispatching, **even if they were not yet in progress when the function was called**.

It's useful when the actions you are waiting for are not yet dispatched when you call this
function. For example, suppose action `StartAction` starts a complex process that takes some time
to run and then eventually dispatches an action called `MyFinalAction`.

In this case, you can use `waitAnyActionTypeFinishes` to wait for `MyFinalAction` to
eventually dispatch and finish:

```dart
dispatch(StartAction());
await store.waitAnyActionTypeFinishes([MyFinalAction]);
```

This function also returns the action that completed the promise,
which you can use to check its `status`.
For example, if you want to assert that `MyFinalAction` failed by throwing a `UserException`:

```dart
dispatch(StartAction());
let action = await store.waitAnyActionTypeFinishes([MyFinalAction]);
expect(action.status.isCompleteOk).toBe(false);
expect(action.status.isCompleteFailed).toBe(true);
expect(action.status.originalError).toBeInstanceOf(UserException>);
```

## waitActionCondition

You can use the `waitActionCondition` function to wait until the set of actions in progress,
and the trigger action, meet the given condition.
The condition function should return `true` when the condition is met, and `false` otherwise:

```ts
await store.waitActionCondition(
  (actionsInProgress, triggerAction) => { // Return true or false }
);
```

The trigger action is the action that just entered the set (by being dispatched),
or just left the set (by finishing dispatching).

:::info

The condition is only checked when some action is dispatched or finishes dispatching.
It's not checked every time action statuses change.

:::

When the promise resolves, you get back the set of actions being dispatched that met the condition,
as well as the trigger action. This may be useful in tests.

:::warning

Your condition function should **not** try and modify the set of actions it got in
the `actionsInProgress` parameter. If you do, Kiss will throw an error.

:::

## Optional parameters

Most wait functions above accept these optional parameters:

* If `completeImmediately` is false (the default),
  the function will throw an error if the condition was already true when the function was called.
  Otherwise, the promise will complete immediately and throw no errors.

* The `timeoutMillis` sets the maximum time to wait for the condition to be met.
  By default, it's 10 minutes. To disable it, make it `0` or `-1`.
  If you want, you can modify `TimeoutException.defaultTimeoutMillis` to change the default.

---
