All Projects → deamme → Laco

deamme / Laco

Licence: mit
⚡️Ultra lightweight state management for React and Inferno

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Laco

Freactal
Clean and robust state management for React and React-like libs.
Stars: ✭ 1,676 (+725.62%)
Mutual labels:  state-management, inferno
Cerebral
Declarative state and side effects management for popular JavaScript frameworks
Stars: ✭ 1,946 (+858.62%)
Mutual labels:  state-management, inferno
Reactn
React, but with built-in global state management.
Stars: ✭ 1,906 (+838.92%)
Mutual labels:  state-management
Jquery History
Super-seeded by https://github.com/browserstate/history.js
Stars: ✭ 183 (-9.85%)
Mutual labels:  state-management
Documentation
How does it all fit together?
Stars: ✭ 177 (-12.81%)
Mutual labels:  state-management
Dop
JavaScript implementation for Distributed Object Protocol
Stars: ✭ 163 (-19.7%)
Mutual labels:  state-management
Observable Slim
Observable Slim is a singleton that utilizes ES6 Proxies to observe changes made to an object and any nested children of that object. It is intended to assist with state management and one-way data binding.
Stars: ✭ 178 (-12.32%)
Mutual labels:  state-management
Mag.js
MagJS - Modular Application Glue
Stars: ✭ 157 (-22.66%)
Mutual labels:  state-management
Reto
Flexible and efficient React Store with hooks.
Stars: ✭ 194 (-4.43%)
Mutual labels:  state-management
Graphql Normalizr
Normalize GraphQL responses for persisting in the client cache/state
Stars: ✭ 175 (-13.79%)
Mutual labels:  state-management
Statefulviewcontroller
Placeholder views based on content, loading, error or empty states
Stars: ✭ 2,139 (+953.69%)
Mutual labels:  state-management
Spaceace
A fancy immutable storage library for JavaScript
Stars: ✭ 167 (-17.73%)
Mutual labels:  state-management
Create flutter provider app
A boilerplate project created in Flutter using Provider and Firebase.
Stars: ✭ 180 (-11.33%)
Mutual labels:  state-management
React Rewind
Time Travel Debugger for React useReducer
Stars: ✭ 159 (-21.67%)
Mutual labels:  state-management
Frideos flutter
An all-in-one Fllutter package for state management, reactive objects, animations, effects, timed widgets etc.
Stars: ✭ 187 (-7.88%)
Mutual labels:  state-management
Redux Rs
📦 A Rust implementation of Redux.
Stars: ✭ 158 (-22.17%)
Mutual labels:  state-management
Hgplaceholders
Nice library to show placeholders and Empty States for any UITableView/UICollectionView in your project
Stars: ✭ 2,048 (+908.87%)
Mutual labels:  state-management
State Machine Component
⚙️ State machine -powered components in 250 bytes
Stars: ✭ 178 (-12.32%)
Mutual labels:  state-management
Stamen
A React state management library based on Hooks
Stars: ✭ 196 (-3.45%)
Mutual labels:  state-management
Flutter book
Flutter1.17.x book App,使用Mobx数据管理器支持Android和iOS,使用库json_serializable、json_annotation、dio。
Stars: ✭ 190 (-6.4%)
Mutual labels:  state-management

Laco

npm version travis

Very simple and powerful state management solution for React and Inferno.

(Inferno doesn't have hooks support but you can use the Subscribe component.)

Set up your stores and subscribe to them. Easy as that!

Check out the introductory blog post.

npm install laco

npm install laco-react or npm install laco-inferno

Summary

Example

import { Store } from 'laco'
import { useStore } from 'laco-react'

// Creating a new store with an initial state { count: 0 }
const CounterStore = new Store({ count: 0 })

// Implementing some actions to update the store
const increment = () => CounterStore.set(state => ({ count: state.count + 1 }))
const decrement = () => CounterStore.set(state => ({ count: state.count - 1 }))

const Counter = () => {
  const state = useStore(CounterStore) // Takes a single store
  return (
    <div>
      <button onClick={decrement}>-</button>
      <span>{state.count}</span>
      <button onClick={increment}>+</button>
    </div>
  )
}

For more examples check the following code sandboxes below or the examples folder.

Code sandboxes using hooks:

Code sandboxes using render props:

Redux DevTools Extension

Check out Redux DevTools Extension.

Check out my post on dev.to about debugging etc..

Time travel

Just click on the stopwatch icon and you will get a slider which you can play with. That's it! :)

React Native Debugger

Check out React Native Debugger.

Time travel

Works as you would expect :)!

API

Store(initialState: Object, name?: String)

// Initializing a new store with an initial state and a name:
const NewStore = Store({ count: 0 }, 'Counter')

The name is optional and is used to get an overview of action and store relationship in Redux DevTools Extension. Action names for the Store will now show up as Counter - ${actionType} in DevTools Extension where as before only ${actionType} was shown.

Store.get()

// Getting the state of the store
Store.get()

Returns an object which could be something like { count: 0 } following the example.

Store.set(state: Function, info?: String)

// Setting a new state and passing an optional action name "increment"
Store.set(state => {
  count: state.count + 1
}, 'increment')

Store.replace(state: Function, info?: String)

Immutability is taking care of to a certain extent behind the scenes with the spread operator with Store.set() but you might want more control over the state. You can do this by using Store.replace() like so:

// Setting a new state and passing an optional action name "increment"
Store.replace(state => {
  /* return modified state */
}, 'increment')

Store.setCondition(condition: Function)

// Setting a condition to prevent count from going below 0 when `actionType` is `Decrement`
CounterStore.setCondition((state, actionType) => {
  if (state.count < 0 && actionType === 'Decrement') {
    // Returning a falsy value will prevent the state from changing
    return false
  }

  // For every other `actionTypes` such as `SudoDecrement` will change the state
  return state
})

Setting a condition on a store will make every Store.set() call go through the condition first.

Store.reset()

// Resets the store to initial state
Store.reset()

A good practice when testing is to call reset() on a store before using the store in a test. This takes care of some edge cases that you might run into. The reason for this is that Laco is using a global object behind the scenes to store all of your stores states into one big object. Redux also operates on one global object which makes time travel possible.

Store.dispatch(value: any, info: String)

// Dispatching an action that does not change the state of the store
Store.dispatch(changeLocation(), 'Location change')

You might want to dispatch an action that is associated with a certain store but don't want to change the state. The action will in this case be shown as StoreName - Location change.

dispatch(value: any, info: String)

import { dispatch } from 'laco'

// Dispatching a global action that does not change any state
dispatch(changeLocation(), 'Location change')

You might want to dispatch a global action that is NOT associated with any store. The action will in this case just be shown as Location change.

getGlobalState()

import { getGlobalState } from 'laco'

getGlobalState()

Returns the global object that holds every state - mostly used for rehydration when doing server-side rendering (SSR).

resetGlobalState()

import { resetGlobalState } from 'laco'

resetGlobalState()

Resets the global state to an empty object.

replaceGlobalState()

import { replaceGlobalState } from 'laco'

const newGlobalState = { 0: { test: true } }

replaceGlobalState(newGlobalState)

Replaces the global state completely - mostly used for rehydration when doing server-side rendering (SSR).

<Subscribe />

Props

  • to - Array of stores you want to subscribe to
import { Subscribe } from 'laco-react' // or 'laco-inferno'

<Subscribe to={[CounterStore]}>
  {({ count }) => (
    <div>
      <button onClick={decrement}>-</button>
      <span>{count}</span>
      <button onClick={increment}>+</button>
    </div>
  )}
</Subscribe>

The Subscribe component is making use of the new render prop idea. Related articles:

useStore()

import { Store } from 'laco'
import { useStore } from 'laco-react'

const CounterStore = new Store({ count: 0 })

const Counter = () => {
  const state = useStore(CounterStore) // Takes a single store
  return <div>{state.count}</div>
}

useStores()

import { Store } from 'laco'
import { useStores } from 'laco-react'

const CounterStore = new Store({ count: 0 })
const AnotherStore = new Store({ test: "hello" })

const Counter = () => {
                                    // Takes an array of stores
  const [counterState, anotherState] = useStores([CounterStore, AnotherStore])
  return <div>{anotherState.test + counterState.count}</div>
}

Rehydration

When doing server-side rendering (SSR) it's important to preserve the state from the server to the client.

Please follow this Redux guide.

On the server: Instead of doing store.getState() you will just use getGlobalState().

On the client: Instead of doing createStore(counterApp, preloadedState) you can do replaceGlobalState(preloadedState)

Keep in mind that trying to do SSR rehydration can introduce JS injections if you don't do it right.

The Redux guide solves it by doing JSON.stringify(preloadedState).replace(/</g, '\\u003c'). For another solution look here.

Testing

Testing using tape:

import * as test from 'tape'
import { CounterStore, increment, decrement } from './CounterStore'

test('counter', t => {
  CounterStore.reset()
  t.assert(CounterStore.get().count === 0)

  increment()
  t.assert(CounterStore.get().count === 1)

  decrement()
  t.assert(CounterStore.get().count === 0)

  t.end()
})

Credits

Heavily inspired by:

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].