• Stars
    star
    693
  • Rank 65,262 (Top 2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 9 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

a reducer enhancer to enable type-agnostic optimistic updates

npm version Build Status Coverage Status

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

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

This will transform your state so it looks like this:

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!

// 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:

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:

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.

More Repositories

1

meatier

🍔 like meteor, but meatier 🍔
JavaScript
3,059
star
2

cashay

💰 Relay for the rest of us 💰
JavaScript
453
star
3

trebuchet-client

A friendly siege weapon to get 2-way communication through tough firewalls and bad mobile networks
TypeScript
177
star
4

redux-operations

Solves challenging redux problems in a clean, understandable, debuggable fasion.
JavaScript
125
star
5

fast-rtc-swarm

A full-mesh WebRTC swarm built on top of fast-rtc-peer
TypeScript
101
star
6

lolliclock

A material design timepicker based on clockpicker
JavaScript
40
star
7

fast-rtc-peer

a small RTC client for connecting 2 peers
TypeScript
34
star
8

fast-bitset

A fast bitset with some nice methods
JavaScript
34
star
9

cashay-playground

The playground for exploring what's possible with Cashay
JavaScript
33
star
10

EdmondsBlossom

Edmond's maximum weighted matching algorithm (Blossom algorithm) in O(n^3)
JavaScript
30
star
11

redux-socket-cluster

A socket-cluster state snatcher
JavaScript
30
star
12

dataloader-warehouse

A class for sharing dataloaders across GraphQL subscriptions
TypeScript
29
star
13

rich

A decentralized collaborative rich text editor powered by DOM mutations, CRDT, and WebRTC
TypeScript
22
star
14

sanitize-svg

a small script to prevent stored XSS attacks and detect script tags in SVGs
TypeScript
19
star
15

redux-operations-counter-example

An example of solving current redux shortcoming using redux-operations
JavaScript
16
star
16

graphql-trebuchet-client

A graphql client to get your subscriptions through tough firewalls and unreliable mobile networks
TypeScript
14
star
17

react-portal-hoc

A stupid HOC to make a stupid portal so you can make stupid modals
JavaScript
14
star
18

event-source-polyfill

A minimum immplementation of EventSource for IE11 and Edge
TypeScript
13
star
19

react-githubish-mentions

A wrapper for a textarea to offers autocomplete suggestions when triggered by @ or # or whatever
JavaScript
13
star
20

json-deduper

Compress JSON trees by deduplicating nested objects, strings, and numbers
TypeScript
11
star
21

react-hotkey-hoc

mousetrap wrapper for react
JavaScript
10
star
22

dynamic-serializer

crawls a JSON tree replacing dynamic values with a deterministic integer
JavaScript
9
star
23

react-async-hoc

a hoc for async globals
JavaScript
6
star
24

meteor-leaflet-maps

Leaflet, now with lazy loading & namespacing!
JavaScript
6
star
25

relay-linear-publish-queue

Publish changes in the order they're received.
TypeScript
4
star
26

surviveJS-redux

A redux version of the awesome surviveJS tutorial for react & webpack.
JavaScript
3
star
27

hungarian-on3

The hungarian (Kuhn-Munkres) algorithm solved in O(n^3) time
JavaScript
3
star
28

rethinkdb-ts-migrate

Migrations for rethinkdb-ts
TypeScript
3
star
29

meteor-vital-signs

JavaScript
2
star
30

meteorTooltips

Meteor tooltips that turn any template into a tooltip
JavaScript
2
star
31

visage

Signaling + SFU for turnkey WRTC (WIP)
TypeScript
2
star
32

event-target-polyfill

EventTarget polyfill for IE11 and Edge from https://developer.mozilla.org/en-US/docs/Web/API/EventTarget#Example
TypeScript
2
star
33

hepha

Aphrodite for global styles
JavaScript
1
star
34

performant-material-input

A feature-rich material design input box with hardware acceleration
CSS
1
star
35

todo-modern-subs

JavaScript
1
star