@smalldots/create-container

9.1.0 • Public • Published

Create Container

npm version

Predictable state containers for React apps

Philosophy

Redux is awesome, but sometimes it might not be the best fit. MobX is awesome, but sometimes it might not be the best fit.

Wouldn't it be great if you could choose a different state management architecture for the different features you are building?

For example, for a simple theme/language store Redux would be overkill, but it shines for stores that receives data through a set of websocket events.

That's the reason we've built this library, you can create multiple stores (containers) having the option to choose the best architecture for the job.

Installation

npm install --save @smalldots/create-container

Import Cost

Import Cost

Demos

Infinity Architecture (optional)

You have the choice to use the simplest container version, createContainer, the Redux inspired version, createReduxContainer or createInfinityContainer, which implements the Infinity Architecture:

Infinity Architecture Preview

How to start mutations when mounting?

It's easy. You can play with this example: https://codesandbox.io/s/n0nv2vo21l

import React from "react"
import { render } from "react-dom"

import { createContainer, Component } from "@smalldots/create-container"

const CounterContainer = createContainer({
  initialState: { count: 0 },
  mutations: {
    increment: () => state => ({ count: state.count + 1 }),
    decrement: () => state => ({ count: state.count - 1 })
  },
  effects: {
    incrementForever: () => ({ increment }) => setInterval(increment, 1000),
    maybeDecrement: () => ({ decrement }) => Math.random() <= 0.2 && decrement()
  }
})

const App = () => (
  <CounterContainer.Provider>
    <CounterContainer.Consumer>
      {({ count, incrementForever, maybeDecrement }) => (
        <Component onMount={incrementForever} onUpdate={maybeDecrement}>
          <p>
            Current count: {count}
            <br />
            <small>
              (It has a 20% chance of pausing for a while, you are not crazy :D)
            </small>
          </p>
        </Component>
      )}
    </CounterContainer.Consumer>
  </CounterContainer.Provider>
)

render(<App />, document.getElementById("root"))

How to compose multiple containers?

It's easy. You can play with this example: https://codesandbox.io/s/pp8494k03q

import React, { Fragment } from "react"
import { render } from "react-dom"

import { createContainer, Compose } from "@smalldots/create-container"

const CounterContainer = createContainer({
  initialState: { count: 0 },
  mutations: {
    increment: () => state => ({ count: state.count + 1 }),
    decrement: () => state => ({ count: state.count - 1 })
  }
})

const TodoContainer = createContainer({
  initialState: { todos: [] },
  mutations: {
    addTodo: () => state => ({
      todos: [
        { id: `${new Date().getTime()}.${Math.ceil(Math.random() * 1000)}` },
        ...state.todos
      ]
    })
  }
})

const App = () => (
  <Compose
    components={[<CounterContainer.Provider />, <TodoContainer.Provider />]}
  >
    <Compose
      components={[<CounterContainer.Consumer />, <TodoContainer.Consumer />]}
    >
      {({ count, increment, decrement }, { todos, addTodo }) => (
        <Fragment>
          <p>Current count: {count}</p>
          <button onClick={increment}>Increment</button>
          <button onClick={decrement}>Decrement</button>
          <hr />
          <button onClick={addTodo}>Add Todo</button>
          <pre>{JSON.stringify(todos, null, 2)}</pre>
        </Fragment>
      )}
    </Compose>
  </Compose>
)

render(<App />, document.getElementById("root"))

Usage

import React, { Fragment } from "react"
import { render } from "react-dom"

import {
  createContainer,
  createReduxContainer,
  createInfinityContainer,
  Compose
} from "@smalldots/create-container"

const CounterContainer = createContainer({
  initialState: { count: 0 },
  selectors: {
    getNextCount: state => state.count + 1
  },
  mutations: {
    increment: () => state => ({ count: state.count + 1 }),
    decrement: () => state => ({ count: state.count - 1 })
  },
  effects: {
    gamify: () => ({ increment, decrement, setState }) =>
      setTimeout(() => {
        Math.random() > 0.5 ? increment() : decrement()
        setState(state => ({ count: state.count + Math.random() }))
      }, 500)
  }
})

const ReduxCounterContainer = createReduxContainer({
  initialState: { count: 0 },
  reducer: {
    INCREMENT: state => ({ count: state.count + 1 }),
    DECREMENT: state => ({ count: state.count - 1 }),
    RANDOMIZE: state => ({ count: state.count + Math.random() })
  },
  selectors: {
    getNextCount: state => state.count + 1
  },
  actions: {
    increment: "INCREMENT",
    decrement: "DECREMENT",
    randomize: "RANDOMIZE"
  },
  effects: {
    gamify: () => ({ increment, decrement, randomize }) =>
      setTimeout(() => {
        Math.random() > 0.5 ? increment() : decrement()
        randomize()
      }, 500)
  }
})

const InfinityCounterContainer = createInfinityContainer({
  initialState: { count: 0 },
  reducer: {
    INCREMENT: state => ({ count: state.count + 1 }),
    DECREMENT: state => ({ count: state.count - 1 }),
    RANDOMIZE: state => ({ count: state.count + Math.random() })
  },
  selectors: {
    getCount: state => state.count,
    getNextCount: state => state.count + 1
  },
  events: {
    incrementClicked: "INCREMENT_CLICKED",
    decrementClicked: "DECREMENT_CLICKED",
    gamifyClicked: "GAMIFY_CLICKED"
  },
  listeners: {
    INCREMENT_CLICKED: ({ increment }) => increment(),
    DECREMENT_CLICKED: ({ decrement }) => decrement(),
    GAMIFY_CLICKED: ({ gamify }) => gamify()
  },
  commands: {
    increment: "INCREMENT",
    decrement: "DECREMENT",
    randomize: "RANDOMIZE"
  },
  effects: {
    gamify: () => ({ increment, decrement, randomize }) =>
      setTimeout(() => {
        Math.random() > 0.5 ? increment() : decrement()
        randomize()
      }, 500)
  }
})

const Counter = ({ count, nextCount, onIncrement, onDecrement, onGamify }) => (
  <Fragment>
    <p>Current count: {count}</p>
    <p>If you click "Increment" the next count will be: {nextCount}</p>
    <button onClick={onIncrement}>Increment</button>
    <button onClick={onDecrement}>Decrement</button>
    <button onClick={onGamify}>Gamify</button>
  </Fragment>
)

const CounterContainerDemo = () => (
  <CounterContainer.Consumer>
    {({ count, getNextCount, increment, decrement, gamify }) => (
      <Counter
        count={count}
        nextCount={getNextCount()}
        onIncrement={increment}
        onDecrement={decrement}
        onGamify={gamify}
      />
    )}
  </CounterContainer.Consumer>
)

const ReduxCounterContainerDemo = () => (
  <ReduxCounterContainer.Consumer>
    {({ count, getNextCount, increment, decrement, gamify }) => (
      <Counter
        count={count}
        nextCount={getNextCount()}
        onIncrement={increment}
        onDecrement={decrement}
        onGamify={gamify}
      />
    )}
  </ReduxCounterContainer.Consumer>
)

const InfinityCounterContainerDemo = () => (
  <InfinityCounterContainer.Consumer>
    {({
      getCount,
      getNextCount,
      incrementClicked,
      decrementClicked,
      gamifyClicked
    }) => (
      <Counter
        count={getCount()}
        nextCount={getNextCount()}
        onIncrement={incrementClicked}
        onDecrement={decrementClicked}
        onGamify={gamifyClicked}
      />
    )}
  </InfinityCounterContainer.Consumer>
)

const App = () => (
  <Compose
    components={[
      <CounterContainer.Provider />,
      <ReduxCounterContainer.Provider />,
      <InfinityCounterContainer.Provider />
    ]}
  >
    <h1>Container Demo</h1>
    <CounterContainerDemo />
    <h1>Redux Container Demo</h1>
    <ReduxCounterContainerDemo />
    <h1>Infinity Container Demo</h1>
    <InfinityCounterContainerDemo />
  </Compose>
)

render(<App />, document.getElementById("root"))

Package Sidebar

Install

npm i @smalldots/create-container

Weekly Downloads

1

Version

9.1.0

License

MIT

Unpacked Size

79.4 kB

Total Files

44

Last publish

Collaborators

  • hnordt