Skip to content
Go to file

English | 简体中文

⚡️ State management that tailored for react, it is simple, predictable, progressive and efficient.


Concent is an amazing state management tool, supported by a healthy middleware ecosystem and excellent devtools. It is a predictable, zero-invasive, progressive, high-performance react development framework!

Concent encourages simplicity. It saves you the hassle of creating boilerplate code and gives powerful tools with a moderate learning curve, suitable for both experienced and inexperienced developers alike.


💻 Playground

Key features snippet

Online case

👨🏽‍💻 Docs

visit official website to learn more.

📦Quick start

Make sure you have installed nodejs


$ npm i --save concent

or yarn command

$ yarn add concent

Minimal example

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

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

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})
    // here if read num, it means current ins render dep keys is ['num']
    return <button onClick={}>{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 (
      <ClsComp />
      <FnComp />

Complete example

Move logic to reducer and define computed,watch,lifecycle
try edit this demo👉better js demo👉better ts demo

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

  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) => {
        // hate string literal? see
        await ac.dispatch("add"); 
        await ac.dispatch("addBig");
    watch: {
      numChange: defWatch(({ num }, o) => console.log(`from ${o.num} to ${num}`), {immediate:true})
    lifecycle: {
      // loaded: (dispatch) => dispatch("initState"), // triggered when module loaded
      mounted: (dispatch) => dispatch("initState"), // triggered when the first ins of counter module mounted
      willUnmount: (dispatch) => dispatch("initState") // triggered when the last ins of counter module unmount

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

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

🐚Architecture diagram

How concent component ins works

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

🎲Code example

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


concent is released under the MIT License.

You can’t perform that action at this time.