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

@smalldots/create-container

v9.1.0

Published

Predictable state containers for React apps

Downloads

81

Readme

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

  • Simple counter: https://codesandbox.io/s/03znoqjz4w
  • Static and dynamic forms: https://codesandbox.io/s/n79lpw1l1p
  • Decoupling state, behavior and presentation: https://codesandbox.io/s/6lwyx2p69k
  • Container composition: https://codesandbox.io/s/pp8494k03q
  • Starting mutations when mounting: https://codesandbox.io/s/n0nv2vo21l
  • Entity container: https://codesandbox.io/s/mzyljzlxj8
  • Using high order containers to create multiple counters: https://codesandbox.io/s/yqrxnk48x1
  • Advanced example (data fetching + entities handling): https://codesandbox.io/s/1vn8j7yq83

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"))