@ice/store-next
TypeScript icon, indicating that this package has built-in type declarations

0.1.0 • Public • Published

English | 简体中文

Notice: icestore-next is still in the experimental stage, please do not use it in the production environment!!!

icestore-next

Lightweight React state management library based on React Hooks.

NPM version Package Quality build status NPM downloads Known Vulnerabilities David deps

🕹 CodeSandbox demos 🕹
Counter Todos

Introduction

icestore-next is a lightweight React state management library based on hooks. It has the following core features:

  • Minimal & Familiar API: No additional learning costs, easy to get started with the knowledge of React Hooks.
  • Centralization: Easy to initialize data and support model interaction.
  • Readonly API: Supports read-only state without subscribing to updates.
  • Great Compatibility: Class Component Support && Perfect TypeScript Support.

Basic example

import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from '@ice/store-next';

// 1️⃣ Create a custom hook as usual
function useCounter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);

  return {
    count,
    increment,
  };
}

const models = {
  counter: useCounter,
};

// 2️⃣ Create the store
const store = createStore(models);

// 3️⃣ Consume model
const { useModel } = store;
function Button() {
  const { increment } = useModel('counter');
  return (
    <button type="button" onClick={increment}> + </button>
  );
}
function Count() {
  const { count } = useModel('counter');
  return (<span>{count}</span>);
}

// 4️⃣ Wrap your components with Provider
const { Provider } = store;
function App() {
  return (
    <Provider>
      <Count />
      <Button />
    </Provider>
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

Installation

icestore requires React 16.8.0 or later.

npm install @ice/store-next --save

Advanced Usages

Readonly

In some scenarios, you may only want to call the method returned by the model to update the state instead of subscribing to the update of the model state. For example, the button component in the "Basic example", you do not consume the state of the model in the component, so you may not expect the change of the state of the model to trigger the re-rende of the component.

At this time, you can use the getmodel API, check following example and compare them with the above example:

const { getModel } = store;
function Button() {
  function handleIncrement() {
    getModel('counter').increment();
  }
  return (
    <button type="button" onClick={handleIncrement}> + </button>
  );
}

Model Interaction

In some scenarios, you might expect a state change of model A to trigger a state update of model B. We call this behavior as "Model Interaction".

For example:

  • We have a todos model that records all tasks.
  • We have a user model, in which there is a todos field, which records the number of tasks owned by the current user.
  • Whenever the todos model's tasks changes, the number of tasks held by users needs to be kept in sync.

State subscription

import { useEffect, useState } from 'react';
import produce from 'immer';
import '@/store';

function useUser() {
  const [state, setState] = useState({ todos: 0 });
  const [todos] = store.useModel('todos');

  useEffect(() => {
    setState(produce((draft) => {
      draft.todos = todos.length;
    }));
  }, [ todos ]);

  return [state, setState];
}

Method Call

import { useState } from 'react';
import produce from 'immer';
import '@/store';

function useTodos() {
  const [state, setState] = useState([
    {
      name: 'angular',
    },
  ]);

  function setTodos(todos) {
    setState(todos);

    const [, setUser] = store.getModel('user');
    setUser(produce((draft) => {
      draft.todos = todos.length;
    }));
  }
 
  return [state, { setTodos }];
}

Class Component Support

import { Component } from 'react';
import store from '@/store';
import useTodos from '@/models/todos';

const { withModel } = store;

interface MapModelToProp {
  todos: ReturnType<typeof useTodos>; // This field is automatically added by withModel
}

interface CustomProp {
  title: string; // User defined props
}

type Props = CustomProp & MapModelToProp;

class Todos extends Component<Props> {
  render() {
    const { title, todos } = this.props;
    const [ state, actions ] = todos;
    return (
      <div>
        {
          state.map(({ name }, index) => {
            return (<div key={index}>
              {name}
              <button onClick={() => actions.remove(index)}>
                Remove
              </button>
            </div>);
          })
        }
      </div>
    );
  }
}

export default withModel('todos')<MapModelToProp, Props>(Todos);

Browser Compatibility

Chrome Firefox Edge IE Safari Opera UC
9+

Contributors

Feel free to report any questions as an issue, we'd love to have your helping hand on icestore.

If you're interested in icestore, see CONTRIBUTING.md for more information to learn how to get started.

Community

DingTalk community GitHub issues Gitter
issues gitter

License

MIT

Readme

Keywords

Package Sidebar

Install

npm i @ice/store-next

Weekly Downloads

1

Version

0.1.0

License

MIT

Unpacked Size

34 kB

Total Files

31

Last publish

Collaborators

  • linbudu
  • answershuto
  • chenjun1011
  • luhengchang228
  • sobear
  • clarkxia
  • rax-publisher