All Projects → gaoding-inc → Stateshot

gaoding-inc / Stateshot

💾 Non-aggressive history state management with structure sharing.

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Stateshot

Concent
State management that tailored for react, it is simple, predictable, progressive and efficient.
Stars: ✭ 882 (+589.06%)
Mutual labels:  state-management, state
Vue State Store
📮 VSS (Vue State Store) - Vue State Management (with Publish & Subscribe pattern)
Stars: ✭ 128 (+0%)
Mutual labels:  state-management, state
Use Global Context
A new way to use “useContext” better
Stars: ✭ 34 (-73.44%)
Mutual labels:  state-management, state
Pullstate
Simple state stores using immer and React hooks - re-use parts of your state by pulling it anywhere you like!
Stars: ✭ 683 (+433.59%)
Mutual labels:  state-management, state
Radon
Object oriented state management solution for front-end development.
Stars: ✭ 80 (-37.5%)
Mutual labels:  state-management, state
Redux Tree
An alternative way to compose Redux reducers
Stars: ✭ 23 (-82.03%)
Mutual labels:  state-management, state
Alveron
Elm & Reason inspired state management for React
Stars: ✭ 57 (-55.47%)
Mutual labels:  state-management, state
Machinery
State machine thin layer for structs (+ GUI for Phoenix apps)
Stars: ✭ 367 (+186.72%)
Mutual labels:  state-management, state
React Composition Api
🎨 Simple React state management. Made with @vue/reactivity and ❤️.
Stars: ✭ 67 (-47.66%)
Mutual labels:  state-management, state
Countries States Cities Database
🌍 World countries, states, regions, provinces, cities, towns in JSON, SQL, XML, PLIST, YAML, and CSV. All Countries, States, Cities with ISO2, ISO3, Country Code, Phone Code, Capital, Native Language, Timezones, Latitude, Longitude, Region, Subregion, Flag Emoji, and Currency. #countries #states #cities
Stars: ✭ 1,130 (+782.81%)
Mutual labels:  json, state
Little State Machine
📠 React custom hook for persist state management
Stars: ✭ 654 (+410.94%)
Mutual labels:  state-management, state
Reworm
🍫 the simplest way to manage state
Stars: ✭ 1,467 (+1046.09%)
Mutual labels:  state-management, state
Reatom
State manager with a focus of all needs
Stars: ✭ 567 (+342.97%)
Mutual labels:  state-management, state
Revived
A redux-inspired predictable state container for python
Stars: ✭ 12 (-90.62%)
Mutual labels:  state-management, state
Westore
更好的小程序项目架构
Stars: ✭ 3,897 (+2944.53%)
Mutual labels:  state-management, state
Duix
A State Manager focused on KISS and Pareto's Principle
Stars: ✭ 48 (-62.5%)
Mutual labels:  state-management, state
Redux Orm
A small, simple and immutable ORM to manage relational data in your Redux store.
Stars: ✭ 2,922 (+2182.81%)
Mutual labels:  state-management, state
Effector
The state manager ☄️
Stars: ✭ 3,572 (+2690.63%)
Mutual labels:  state-management, state
Yewdux
Redux-like state containers for Yew apps
Stars: ✭ 58 (-54.69%)
Mutual labels:  state-management, state
Rki Covid Api
🦠🇩🇪📈 An API for the spread of covid-19 in Germany. Data from Robert-Koch-Institut.
Stars: ✭ 98 (-23.44%)
Mutual labels:  json, state

StateShot

💾 Non-aggressive history state management with structure sharing.

Coverage Status

stateshot

Just push your states into StateShot and undo / redo them!

Getting Started

Install via NPM:

npm i stateshot

Basic usage:

import { History } from 'stateshot'

const state = { a: 1, b: 2 }

const history = new History()
history.pushSync(state) // the terser `history.push` API is async

state.a = 2 // mutation!
history.pushSync(state)

history.get() // { a: 2, b: 2 }
history.undo().get() // { a: 1, b: 2 }
history.redo().get() // { a: 2, b: 2 }

Concepts

For history state management, the top need is the undo / redo API. That's what StateShot provides out of the box, which can be simply described in image below:

stateshot

Trivial, right? While in real world projects, the price on saving full state is high. Immutable data structure is known to be suitable for this, since it can share data structure in different references. However, this requires fully adaptation to immutable libs - can be aggressive indeed.

StateShot supports sharable data structure under its tiny API surface. The core concept is to serialize state node into chunks, computing chunks' hash and share same space if hash meets:

stateshot

Besides the flexible rule-based transforming StateShot supports, it also provides another low-hanging fruit optimization for SPA apps. Suppose your root state is composed of multi "pages", editing on one page does not affect other pages. In this case computing hash on full state is inefficient. As a solution, you can simply specify a pickIndex on pushing new state, telling the lib which page to record:

stateshot

With this hint, only the affected child's hash will be re-computed. Other children simply remains the same with previous record.

API

History

new History(options?: Options)

Main class for state management, option includes:

  • initialState - Optional initial state.
  • rules - Optional rules array for optimizing data transforming.
  • delay - Debounce time for push in milliseconds, 50 by default.
  • maxLength - Max length saving history states, 100 by default.
  • useChunks - Whether serializing state data into chunks. true by default.
  • onChange - Fired when pushing / pulling states with changed state passed in.

If you want to use StateShot with immutable data, simply set useChunks to false and new reference to state will be directly saved as records.

push

(state: State, pickIndex?: number) => Promise<History>

Push state data into history, using pushSync under the hood. state doesn't have to be JSON serializable since you can define rules to parse it.

If pickIndex is specified, only this index of state's child will be serialized. Other children will be copied from previous record. This optimization only happens if previous records exists.

pushSync

(state: State, pickIndex?: number) => History

Push state into history stack immediately. pickIndex also supported.

undo

() => History

Undo a record if possible, supports chaining, e.g., undo().undo().get().

redo

() => History

Redo a record if possible, also supports chaining,

hasUndo

boolean

Whether current state has undo records before.

hasRedo

boolean

Whether current state has redo records after.

length

number

Valid record length of current instance.

get

() => State

Pull out a history state from records.

reset

() => History

Clear internal data structure.

Rule

{ match: function, toRecord: function, fromRecord: function }

By defining rules you can specify how to transform between states and internal "chunks". Chunks are used for structure sharing.

Rules are only designed for optimization. You don't have to learn or use them unless you've encountered performance bottleneck.

match

node: StateNode => boolean

Defines whether a rule can be matched. For example, if you're saving a vDOM state with different type field, just define some rules like node => node.type === 'image' or node => node.type === 'text'.

toRecord

StateNode => { chunks: Chunks, children: Children }

For matched node, chunks is the serializable data we transform it into, and children picks out its children for further traversing (By default we traverse the children field in each state node, you can customize this behavior by providing code like children: node.elements or so). Usually one chunk per node is enough, but you can split a node into multi chunks in this manner:

const state = {
  type: 'container',
  children: [
    { type: 'image', left: 100, top: 100, image: 'foo' },
    { type: 'image', left: 200, top: 200, image: 'bar' },
    { type: 'image', left: 300, top: 300, image: 'baz' }
  ]
}

// Suppose `image` is a heavy field, we can split this field as a chunk.
const toRecord = node => ({
  chunks: [
    { ...node, image: null },
    node.image
  ]
})

fromRecord

{ chunks: Chunks, children: Children } => StateNode

Parse the chunks back into the state node. For case before:

// Recover state node from multi chunks.
const fromRecord = ({ chunks, children }) => ({
  ...chunks[0],
  image: chunks[1]
})

const rule = {
  match: ({ type }) => type === 'image',
  toRecord,
  fromRecord
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].