npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-replicate

v3.0.9

Published

Creates a Redux store enhancer designed to replicate actions and states. Declaratively connect application state to data sources and create efficient, scalable, and reliable software with minimal effort.

Downloads

37

Readme

redux-replicate

build status npm version npm downloads

Creates a Redux store enhancer designed to replicate actions and states.

Table of contents

  1. Installation
  2. Why?
  3. Exports
  1. Usage
  1. Replicators
  1. Store modifications
  1. Example replicator
  2. Example using react-redux-provide
  3. Example using compose

Installation

npm install redux-replicate --save

Why?

Many times you'll find yourself manually retrieving data and updating the state(s) of your store(s) based on the result. This library allows you to do this automatically and in a modular way.

Replication is a key concept when building any stateful application. When implemented correctly, it allows you to decouple data initialization, storage, and retrieval from your application so that your only concern is rendering its state. It allows you declaratively connect application state to data sources and create efficient, scalable, and reliable software with minimal effort.

You can:

  • Replicate specific state keys to some data source (database, API, etc.) whenever they change.

  • Replicate the entire state to some data source (database, API, etc.) whenever anything changes.

  • Automatically initialize state from some data source, synchronously or asynchronously.

  • Give the server full authority over the initial state and/or allow the client to override the initial state with their own values.

  • Instantly add real-time functionality to keep clients in sync with each other.

  • Instantly add or remove any number of data sources (databases, API, sockets, etc.).

If this is new to you, see Wikipedia's State machine replication page for more about it!

Exports

Default export

  • replicate - Function which creates and returns the store enhancer based on the replication passed to it. See the Usage section below.

Utilities

  • storeKeysEqual - Accepts 2 store keys as arguments and returns true if they match.

Action types

  • GET_INITIAL_STATE - Dispatched immediately before calling a replicator's getInitialState method.

  • GOT_INITIAL_STATE - Dispatched after a replicator calls setState within its getInitialState method.

  • INITIAL_STATE_ERROR - Dispatched when a replicator calls setError within its getInitialState method.

  • REPLICATE_INITIAL_STATE - If using replication.clientState or if a replicator passes undefined to setState within its getInitialState method and the store's default initial state should be replicated (via either replication.create or replication.clientState), this will be dispatched prior to replicating the state (via the replicator's onStateChange method).

  • REPLICATED_INITIAL_STATE - Applicable only in conjunction with REPLICATE_INITIAL_STATE. Dispatched when a replicator calls setState or setStatus within its onStateChange method.

  • REPLICATE_STATE - Dispatched immediately before calling a replicator's onStateChange method.

  • REPLICATED_STATE - Dispatched when a replicator calls setState or setStatus within its onStateChange method.

  • STATE_CHANGE_ERROR - Dispatched when a replicator calls setError within its onStateChange method.

  • SET_STATE - Used internally when setting the state via store.setState, but you can dispatch this action along with nextState if for some reason you need to manually override the store's current state.

  • FULLY_INITIALIZED - Dispatched after replication as fully initialized.

Usage

Call the replicate function (default export) with the following options (as keys within an object) to create a store enhancer.

key

Typically a string, but this can be anything. It's passed to your replicators so they know where and/or how to replicate data. If you're using reducerKeys, each reducerKey is included in an object with this key when calling a replicator's getInitialState and onStateChange methods.

reducerKeys

Optional boolean value, array of strings, or object containing boolean values. This is helpful (and recommended!) if you're using Redux's combineReducers function (or similar) and want to replicate changes to individual keys within the store's state object, rather than the entire state tree.

If an array, it will replicate only the keys within the array.

If an object, it will be compared to the clientState (see below). All keys within the object will be replicated, but only the keys with truthy values will be initialized. This is useful if you want the server to be able to specify a custom initial state while also allowing the client to override the server's initial state.

If true, it will replicate all keys.

If either false or omitted, it will replicate the entire state object when calling getInitialState and onStateChange - i.e., it won't iterate over each reducerKey even when combineReducers is used.

queryable

Optional boolean value or object used for specifying whether or not the key (or reducerKeys if used) should be queryable by value. Defaults to false.

If true and not using reducerKeys, then the key will be queryable by value (current state).

If true and using reducerKeys, then each reducerKey will be queryable by value (current state).

An object specifies which reducerKeys are queryable by value (current state) where the keys within the object correspond to each reducerKey and the values should be truthy.

If false, values (current states) will not be queryable.

create

Set this to true to ensure initial states are replicated.

clientState

Optional object used when determining which reducerKeys should be initialized on the client. This object should be the initial state provided to the client by the server. If no clientState, the client will be able to fully override the initial state with their own replicated values.

replicator

Either a single replicator or an array of replicators. See the Replicators section below.

Replicators

Replicators can:

  • Initialize the state of the store, synchronously and/or asynchronously.

  • Save state changes to data sources.

  • Allow current states to be queryable.

  • Send actions to clients, other servers, etc.

  • Be packaged and easily reusable!

A replicator is a plain object of the following shape.

getInitialState

Optional function to set the store's initial state, synchronously or asynchronously. The following options are passed to it within an object:

Object store,
String reducerKey,
Function setState,
Function setError

If using reducerKeys, this function is called once per initializable reducerKey (passing each reducerKey per call).

If not using reducerKeys, this function is called only once.

Either the setState or the setError function must be called for the store to finish initializing, regardless of whether or not the state exists within the data source.

Example (from redux-replicate-localforage):

const getItemKey = (key, reducerKey) => reducerKey
  ? `${key}/${reducerKey}`
  : key;

const getInitialState = ({ store, reducerKey, setState, setError }) => {
  localforage
    .getItem(getItemKey(store.key, reducerKey))
    .then(state => setState(parse(state)))
    .catch(setError);
};

onReady

Optional function called after initialization. The following options are passed to it within an object:

Object store

Example:

import { storeKeysEqual } from 'redux-replicate';

const onReady = ({ store }) => socket.on('action', ({ key, action }) => {
  if (storeKeysEqual(key, store.key)) {
    store.dispatch(action);
  }
});

onStateChange

Optional function to replicate the state and/or the action upon state changes. The following options are passed to it within an object:

Object store,
String reducerKey,
Mixed state,
Mixed nextState,
Boolean queryable,
Object action,
Mixed clientState,
Mixed create,
Function setState,
Function setStatus,
Function setError

If using reducerKeys, this function is called once per reducerKey with state, nextState, and queryable representing each particular reducerKey.

If not using reducerKeys, this function is called only once.

You should always call either create (if it's a function), setState, setStatus, or setError when the replication has completed.

Example (from redux-replicate-localforage):

const onStateChange = ({
  store,
  reducerKey,
  nextState,
  queryable,
  setStatus,
  setError
}) => {
  localforage
    .setItem(getItemKey(store.key, reducerKey), stringify(nextState))
    .then(() => setStatus())
    .catch(setError);

  if (queryable) {
    // in the case of simple key-value stores like localforage where the
    // ability to query by value doesn't come with it, you can store a
    // custom map of values to keys to be used for querying...
    // in most cases you won't have to implement this functionality though :)
  }
};

postReduction

Optional function to replicate the state and/or the action upon any reduction, regardless of whether or not the store's state has changed. This is called only after initialization. If you want to replicate actions, this is the place to do it. The following options are passed to it within an object:

Object store,
Mixed state,
Mixed nextState,
Object action

This function is only called once per reduction. A quick state !== nextState check here would let you know if any change has taken place, regardless of whether or not you're using reducerKeys.

Example:

function postReduction({ store, state, nextState, action }) {
  if (state !== nextState) {
    socket.emit('action', { key: store.key, action });
  }
}

handleQuery

Optional function to handle some query. The query argument can be specific to your implementation, but it's best to follow convention. In the future, we may solidify a standard for this. The following options are passed to it within an object:

Object store,
Mixed query,
Mixed options,
Function setResult,
Function setError

If using reducerKeys, the query argument should typically be an object containing reducerKey to state (value) pairs.

If not using reducerKeys, the query argument should typically be the entire state.

These are just general guidelines though, as it all really comes down to how your particular database or API is designed to handle queries.

Example:

function handleQuery({ query, options, setResult, setError }) {
  database
    .find(query, options)
    .then(setResult)
    .catch(setError);
}

Store modifications

The enhancer adds the following to the store object.

store.key

The current key.

store.setKey

(String key, Function readyCallback)

Sets the current key. The readyCallback is called after all of the replicators have fully initialized based on the new key.

store.setState

(Mixed nextState)

You typically shouldn't need to use this, as state changes should almost always occur as a result of store.dispatch(action). But it may be useful for keeping a store's state synchronized with some data source which doesn't rely on actions. If using reducerKeys, the nextState is expected to be an object and is merged into the current state, similar to React's setState. If not using reducerKeys, the nextState replaces the current state entirely.

store.replication

An array containing each replication object (options) passed to the replicate function (this package's default export). In most cases, this will contain only a single replication object, but it's mostly used internally for cases where multiple replicate enhancers are applied.

store.onReady

(Function readyCallback)

You can use this if you know your replicator(s) asynchronously initialize the store's state and would like to do something immediately after initialization. The readyCallback will receive the store within an object.

store.readyCallbacks

Used internally for holding each readyCallback passed to store.onReady.

store.initializedReplication

If for some reason you need to know whether or not getInitialState has completed, you can check this boolean property. It will be true after initialization.

Example replicator

See redux-replicate-fs, a replicator that persists the state of your store(s) and handles queries using Node's file system API.

Example using react-redux-provide

Replication and providers work great together! Providers help you fully reap the benefits of replication. Below you can see how, with just a few lines of code, we enable real-time chat functionality and replicate the states of entries and comments to RethinkDB.

// src/replication.js

import rethink from 'redux-replicate-rethink';
import socket from 'redux-replicate-socket';
import * as providers from './providers/index';

// replication `key` defaults to each provider instance's key,
// which may be a function of props and context

providers.entry.replication = {
  reducerKeys: ['time', 'author', 'entry', 'tags'],
  replicator: rethink
};

providers.comment.replication = {
  reducerKeys: ['time', 'author', 'comment', 'for'],
  replicator: rethink
};

providers.message.replication = {
  reducerKeys: ['time', 'author', 'message'],
  replicator: socket
};

Example using compose

import { createStore, combineReducers, compose } from 'redux';
import replicate from 'redux-replicate';
import localforage from 'redux-replicate-localforage';
import reducers from './reducers';

const initialState = {
  wow: 'such storage',
  very: 'cool'
};

const key = 'superCoolStorageUnit';
const reducerKeys = true;
const replicator = localforage;
const replication = replicate({ key, reducerKeys, replicator });
const create = compose(replication)(createStore);
const store = create(combineReducers(reducers), initialState);