/concent

Definitely the ❤️ simplest but ⚡️ strongest state management for react, it is predictable、progressive and efficient.

Primary LanguageJavaScriptMIT LicenseMIT

English | 简体中文

concent


npm version issues open downloads last commit commit activity license:MIT mini bundle size co version followers concent star

Definitely the ❤️ simplest but ⚡️ strongest state management for react, it is predictable、progressive and efficient.

✨Features

Docs

visit official website https://concentjs.github.io/concent-doc to learn more.

📦Quick start

Make sure you have installed nodejs

Install

$ npm i --save concent

or yarn command

$ yarn add concent

Minimal example

import { run } from 'concent';
import { register, useConcent } from 'concent';

run({
  counter: {// declare a moudle named 'counter'
    state: { num: 1, numBig: 100 }, // define state
  },
  // you can also put another module here.
});

@register('counter')
class DemoCls extends React.Component{
  // commit state to store and broadcast to other refs which also belong to counter module
  inc = ()=> this.setState({num: this.state.num + 1})
  render(){
    // here if read num, it means current ins render dep keys is ['num']
    return <button onClick={this.inc}>{this.state.num}</button>
  }
}

function DemoFn(){
  const { state, setState } = useConcent('counter');
  const inc = ()=> setState({num: state.num + 1});
  return <button onClick={inc}>{state.num}</button>
}

export default function App(){
  return (
    <div>
      <ClsComp />
      <FnComp />
    </div>
  );
}

Complete examples

  • Move logic to reducer and define computedwatchlifecycle

try edit this demo、 👉better js demo、👉better ts demo

import { run, defWatch } from 'concent';

run({
  counter: {
    state: { num: 1, numBig: 100 },
    computed: {
      numx2: ({ num }) => num * 2, // only num changed will trigger this fn
      numx2plusBig: ({ numBig }, o, f) => f.cuVal.numx2 + numBig // reuse computed reslult
    },
    reducer: {
      initState: () => ({ num: 8, numBig: 800 }),
      add: (payload, moduleState, actionCtx) => ({ num: moduleState.num + 1 }),
      addBig: (p, m, ac) => ({ numBig: m.numBig + 100 }),
      asyncAdd: async (p, m, ac) => {
        await delay(1000);
        return { num: m.num + 1 };
      },
      addSmallAndBig: async (p, m, ac) => {
        await ac.dispatch("add"); // hate string literal? see https://codesandbox.io/s/combine-reducers-better-7u3t9
        await ac.dispatch("addBig");
      }
    },
    watch: {
      numChange: defWatch(({ num }, o) => console.log(`from ${o.num} to ${num}`), {immediate:true})
    },
    lifecycle: {
      // loaded: (dispatch) => dispatch("initState"), // when module loaded
      mounted: (dispatch) => dispatch("initState"), // when any first ins of counter module mounted will trigger this
      willUnmount: (dispatch) => dispatch("initState") // when last ins of counter module unmount will trigger this
    }
  }
});

@register("counter")
class DemoCls extends React.Component {
  render() {
    // mr is short of moduleReducer, now you can call all counter module reducer fns by mr
    return <button onClick={this.ctx.mr.add}>{this.state.num}</button>;
  }
}

function DemoFn() {
  const { moduleComputed, mr } = useConcent("counter");
  return <button onClick={mr.add}>numx2plusBig: {moduleComputed.numx2plusBig}</button>;
}

💻 Playground

Key features snippet

Real world

Eco system

With middleware and plugin mechanism, you can easily cutomize your common handler for non logic code, or migrate redux eco lib.

Use with react router

Details see here react-router-concent,expose history,you can call it anywhere in your app to enjoy the imperative navigation jump.

react-router-concent online demo

Use with redux-dev-tool

Details see here concent-plugin-redux-devtool,track your state changing history。 redux-dev-tool

Use with plugin-loading

Details see here concent-plugin-loading,control all your reducer function's loading status easily。

concent-plugin-loading online demo


📰 Articles


How concent component ins works