# redux-optimistic-ui

> a reducer enhancer to enable type-agnostic optimistic updates

Latest version **3.1.0** (published 2017-08-31) · MIT license · 0 weekly downloads

## Install

```sh
npm install redux-optimistic-ui
pnpm add redux-optimistic-ui
yarn add redux-optimistic-ui
bun add redux-optimistic-ui
```

## Health

**Score 23/100 (F)** — status: abandoned.

Positive: has types package; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 3.1.0 |
| Published | 2017-08-31 |
| First published | 2016-01-08 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | separate (@types/redux-optimistic-ui) |
| Module format | CommonJS |
| Dependencies | 0 |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 686 |
| Author | Matt Krick |
| Maintainers | mattkrick |
| Keywords | redux, optimistic, latency-compensation, optimistic-ui |

## Links

- npm: https://www.npmjs.com/package/redux-optimistic-ui
- Repository: https://github.com/mattkrick/redux-optimistic-ui
- Homepage: https://github.com/mattkrick/redux-optimistic-ui#readme
- Issues: https://github.com/mattkrick/redux-optimistic-ui/issues
- npm.io page: https://npm.io/package/redux-optimistic-ui

## Recent versions

- 3.1.0 (latest) — 2017-08-31
- 3.0.0 — 2017-08-03
- 2.0.0 — 2017-06-23
- 1.0.0 — 2017-02-17
- 0.5.0 — 2016-12-16
- 0.4.1 — 2016-08-17
- 0.4.0 — 2016-03-22
- 0.3.2 — 2016-02-06
- 0.3.1 — 2016-01-22
- 0.3.0 — 2016-01-10
- 0.2.0 — 2016-01-08
- 0.1.1 — 2016-01-08
- 0.1.0 — 2016-01-08

## README

[![npm version](https://badge.fury.io/js/redux-optimistic-ui.svg)](https://badge.fury.io/js/redux-optimistic-ui)
[![Build Status](https://travis-ci.org/mattkrick/redux-optimistic-ui.svg?branch=master)](https://travis-ci.org/mattkrick/redux-optimistic-ui)
[![Coverage Status](https://coveralls.io/repos/github/mattkrick/redux-optimistic-ui/badge.svg?branch=master)](https://coveralls.io/github/mattkrick/redux-optimistic-ui?branch=master)

# redux-optimistic-ui
a reducer enhancer to enable type-agnostic optimistic updates

## Installation
`yarn add redux-optimistic-ui`

## A what-now?
A reducer enhance is a function you put around a reducer.
It can be your rootReducer (the output from a `combineReducers`) or a nested one.
Optimistic-UI means you update what the client sees before the result comes back from the server.
This makes your app feel super fast, regardless of server location or internet connection speed.

## How's it different from redux-optimist?

| redux-optimistic-ui                                    | redux-optimist                                                    |
|--------------------------------------------------------|-------------------------------------------------------------------|
| reducerEnhancer (wraps your state)                     | reducerExtender (adds an optimist to your state)                  |
| can use immutable.js or anything else                  | must use plain JS objects for your state                          |
| only uses 1 state copy                                 | saves an extra copy of your state for every new optimistic action |
| FSA compliant                                          | not FSA compliant                                                 |
| must wrap your state calls in `ensureState`            | no change necessary to get your state                             |

## Usage

### Feed it your reducer

```js
import {optimistic} from 'redux-optimistic-ui';
return optimistic(reducer);
```

This will transform your state so it looks like this:

```js
state = {
  history: [],
  beforeState: <YOUR PREVIOUS STATE HERE>
  current: <YOUR STATE HERE>
}
```
If the client is not waiting for a response from the server, the following are guaranteed to be true:
- `state.history.length === 0`
- `state.beforeState === undefined`

If you don't need to know if there is an outstanding fetch, you'll never need to use these.

### Update your references to `state`

Since your state is now wrapped, you need `state.current`.
But that sucks. What if you don't enhance the state until the user hits a certain route?
Lucky you! There's a function for that. `ensureState` will give you your state whether it's enhanced or not.
Just wrap all your references to `state` and `getState` with it & you're all set!

```js
// Before
getState().counter

// After (whether you've enhanced your reducer or not)
import {ensureState} from 'redux-optimistic-ui'
ensureState(getState()).counter
```

### Write some middleware

Now comes the fun! Not all of your actions should be optimistic.
Just the ones that fetch something from a server *and have a high probability of success*.
I like real-world examples, so this middleware is a little bit longer than the bare requirements:

```js
import {BEGIN, COMMIT, REVERT} from 'redux-optimistic-ui';

//All my redux action types that are optimistic have the following suffixes, yours may vary
const _SUCCESS = '_SUCCESS';
const _ERROR = '_ERROR';

//Each optimistic item will need a transaction Id to internally match the BEGIN to the COMMIT/REVERT
let nextTransactionID = 0;

// That crazy redux middleware that's 3 functions deep!
export default store => next => action => {
  // FSA compliant
  const {type, meta, payload} = action;

  // For actions that have a high probability of failing, I don't set the flag
  if (!meta || !meta.isOptimistic) return next(action);

  // Now that we know we're optimistically updating the item, give it an ID
  let transactionID = nextTransactionID++;

  // Extend the action.meta to let it know we're beginning an optimistic update
  next(Object.assign({}, action, {meta: {optimistic: {type: BEGIN, id: transactionID}}}));

  // HTTP is boring, I like sending data over sockets, the 3rd arg is a callback
  socket.emit(type, payload, error => {
    // Create a redux action based on the result of the callback
    next({
      type: type + (error ? _ERROR : _SUCCESS),
      error,
      payload,
      meta: {
        //Here's the magic: if there was an error, revert the state, otherwise, commit it
        optimistic: error ? {type: REVERT, id: transactionID} : {type: COMMIT, id: transactionID}
      }
    });
  })
};
```

## Pro tips
Not using an optimistic-ui until a certain route? Using something like `redux-undo` in other parts? Write a little something like this and call it on your asychronous route:

```js
export default (newReducers, reducerEnhancers) => {
  Object.assign(currentReducers, newReducers);
  const reducer = combineReducers({...currentReducers})
  if (reducerEnhancers){
    return Array.isArray(reducerEnhancers) ? compose(...reducerEnhancers)(reducer) : reducerEnhancers(reducer);
  }
  return reducer;
}
```
Now you get an enhanced reducer only where you want it. Neat.

To see how it all comes together, check out https://github.com/mattkrick/meatier.

---
_Source: https://npm.io/package/redux-optimistic-ui · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
