Heridux

The easiest way to use and reuse redux stores or react internal states

Create your store

import Heridux from "@heridux/immer"

const store = new Heridux("counterStore")

store.setInitialState({ counter : 0 })

store.createAction("increment", state => {
  state.counter++
})

store.createAction("decrement", state => {
  state.counter--
})

store.register()

Use it with React

import { useStore, useSelector } from "@heridux/react"

const MyComponent = () => {
  const store = useStore()
  const counter = useSelector(state => state.counter)

  return (
    <div>
      <p>Clicked: <span>{ counter }</span> times</p>
      <button onClick={ () => store.execAction("increment") }>+</button>
      <button onClick={ () => store.execAction("decrement") }>-</button>
    </div>
  )

}

Reuse your store logic

Heridux was created primarily to make redux stores reusable (just like hooks make internal states reusable).

For example, in a large application, you can have several CRUDs that work according to the same logic, but with redux it is difficult to share this code (even if it's a bit easier with Redux Toolkit).

MyStore.js

import Heridux from "@heridux/immer"

export default class MyStore extends Heridux {

  constructor(STATE_PROPERTY) {

    super(STATE_PROPERTY)

    this.setInitialState({ count : 0 })

    this.createAction("increment", state => {
      state.count++
    })

  }
}

store1.js

import Store from "./MyStore"
const store = new MyStore("my first store")

// register in redux store
// with "my first store" as entry key
store.register()

store.execAction("increment")

store2.js

import Store from "./MyStore"
const store = new MyStore("my second store")

// create additional actions if you need
store.createAction("decrement", state => {
  state.count--
})

store.register()

store.execAction("increment")
store.execAction("decrement")

No more hesitation between state and redux

It is sometimes difficult to know in advance whether the state should be stored in the redux store or whether an internal state is sufficient.

With heridux, no more worries : the API is unique, you can choose afterwards whether you want to use redux or an internal state.

With Redux

import Heridux from "@heridux/immer"

const store = new Heridux("entryKey")

store.createAction("increment", state => { state.count++ })

store.register()

export default store

With internal state

import Heridux from "@heridux/immer"

const store = new Heridux() // entry key is not needed

store.createAction("increment", state => { state.count++ })

// don't register in redux store and that's all !

export default store

Easy to use with Immer or Immutable

Even if you don't have to, it is strongly recommended to use the Immer version of Heridux. Alternatively, you can also use the Immutable version of Heridux.

Without ImmerJS or ImmutableJS

import Heridux from "@heridux/core"

const store = new Heridux("entryKey")

store.setInitalState({
  address : { zipCode : "31400", city : "Toulouse" }
})

store.createAction("changeZipCode", (state, { zipCode }) => ({
  ...state,
  address : {
    ...state.address,
    zipCode
  }
}))

With ImmerJS

import Heridux from "@heridux/immer"

const store = new Heridux("entryKey")

store.setInitalState({
  address : { zipCode : "31400", city : "Toulouse" }
})

store.createAction("changeZipCode", (state, { zipCode }) => {
  state.address.zipCode = zipCode
})

With ImmutableJS

import Heridux from "@heridux/immutable"

const store = new Heridux("entryKey")

store.setInitalState({
  address : { zipCode : "31400", city : "Toulouse" }
})

store.createAction("changeZipCode", (state, { zipCode }) => (
  state.setIn(["address", "city"], zipCode)
))

No learning pain

The API is minimalist and mostly the same as React-Redux.

Getting started

Installation

npm install @heridux/core @heridux/immer @heridux/react
# or with yarn
yarn add @heridux/core @heridux/immer @heridux/react

Setting up

With redux from scratch

import Heridux from "@heridux/immer"
import { Provider } from "react-redux"
import App from "./App"

// with Heridux you'll define your reducers later
const reduxStore = Heridux.createReduxStore()

export default () => (
  <Provider store={ reduxStore }>
    <App/>
  </Provider>
)

With an existing redux store

import { createStore } from "redux"
import Heridux from "@heridux/immer"

const reducers = function(state){ /* redux reducer */ return state }
const store = createStore(reducers)

Heridux.connect(store, reducers)

export default store
            

Without redux

If you don't plan to use redux (maybe you should) but only internal states, nothing is required here.

Using Heridux

store.js

import Heridux from "@heridux/immer"

const store = new Store("counter")

store.setInitialState({ counter : 0 })

store.createAction("increment", state => {
  state.counter++
})

store.createAction("decrement", state => {
  state.counter--
})

store.register() // omit this line if you don't want to store your data in redux
// but only in an internal state

export default store

AppPart.js

import { Provider } from "@heridux/react"
import store from "./store"
import MyComponent from "./MyComponent"

export default () => (
  <Provider store={ store }>
    <MyComponent/>
  </Provider>
)

MyComponent.js

import { useStore, useSelector } from "@heridux/react"

const MyComponent = () => {
  const store = useStore()
  const counter = useSelector(state => state.counter)

  return (
    <div>
      <p>Clicked: <span>{ counter }</span> times</p>
      <button onClick={ () => store.execAction("increment") }>+</button>
      <button onClick={ () => store.execAction("decrement") }>-</button>
    </div>
  )
}

Alternatively, you can use a HOC

import { connect } from "@heridux/react"

const MyComponent = ({ store, counter }) => (
  <div>
    <p>Clicked: <span>{ counter }</span> times</p>
    <button onClick={ () => store.execAction("increment") }>+</button>
    <button onClick={ () => store.execAction("decrement") }>-</button>
  </div>
)

const mapStateToProps = state => ({
  counter : state.counter
})

export default connect(mapStateToProps)(MyComponent)


FAQ

Why do I get the error "Redux store has not been defined as reduxStore key of Heridux" ?

Make sure Heridux is connected to Redux before importing any file that uses it. You'll probably have to create a separate file, like in this example.