Use concent in react, share state easily and enjoy exact update。
Tags: react, concent
Published: 2020-03-09 13:48:18

![](https://github.com/fantasticsoul/assets/blob/master/temp/17928.png?raw=true)
Star [Concent](https://github.com/concentjs/concent) if you are interested in it, I will appreciate it greatly.


This is a small article to let more people know why I write the below slogan for [concent](https://github.com/concentjs/concent)    
 `a predictable、zero-cost-use、progressive、high performance's enhanced state management solution`

We all know the most 2 popular state management `redux` and `mobx`, but have we thought that is there another one can just combine with react very very naturally? what I mean is that it is just like a part of a react when we use it, no more complex concept, and write a high performance app easily and the same time.

So I start to build the project `concent`, its all features is optional but born for different scenes, it will come into your eyes just at a very right timing, all you have to do is just start with a very simple case and  
finally you will find what awesome features that `concent` will offer you.


## Let's start
Firstly we write a component `HelloWorld`, that's a very easy example ready for the most react starter.
```js
class HelloWordComp extends React.Component{
   state = {greeting:'hello world'}
   onChange = (e)=> this.setState({greeting:e.target.value})
   render(){
       return <input value={this.state.greeting} onChange={this.onChange}/>
   }
}
```
After hook born, we may write it like this
```js
function HelloWorldFn(){
    const [greeting, setter] = React.useState('hello world');
    const onChange = (e)=> setter(e.target.value);
    return <input value={greeting} onChange={onChange}/>
}
```

## Share the state
So what should we do if we want the 2 component ins share the state, react tell us to lift up the state to props, but if there are many many nested component it will become a very big coding trouble.

Now let `Concent` give you its answer.
- step 1, configure a module named `hello`
```js
import { run } from 'concent';

run({
    hello: {
        state: {greeting: 'hello world'}
    }
})
```
- step 2, register the target component to concent component
```js
// for class, we can change nothing but only add a register decorator
@register('hello')
class HelloWordComp extends React.Component{...}


// for function, we use hook useConcent
function HelloWorldFn(){
    const {state, setState} = useConcent('hello');
    const onChange = (e)=> setState({greeting:e.target.value});
    return <input value={state.greeting} onChange={onChange}/>
}
```
- step 3, initialize them
```js
function App(){
   return (
       <div>
           <HelloWordComp />
           <HelloWordComp />
           <HelloWorldFn />
           <HelloWorldFn />
       </div>
   )
}
```
now if you type content at any one of their input box, the rest will be trigger re-rendered.

below is the all code pic
![code](https://raw.githubusercontent.com/fantasticsoul/assets/master/temp/xxxxxx1.png)
and let us see the effect
![effect](https://github.com/fantasticsoul/assets/blob/master/temp/Mar-09-2020%2021-04-07.gif?raw=true)
[you can also edit the demo here](https://codesandbox.io/s/dev-demo-1-9off7)

## Hate boring setState?
if you hate writing many `setState`, you can use `sync` series api.
```js
function HelloWorldFn(){
    const {state, setState} = useConcent('hello');
    const onChange = (e)=> setState({greeting:e.target.value});
    return <input value={state.greeting} onChange={onChange}/>
}

// change to 
function HelloWorldFn(){
    const {state, sync} = useConcent('hello');
    return <input value={state.greeting} onChange={sync('greeting')}/>
}
```

## Extract logic to reducer
Actually this is not the point I want to talk about, so about reducer you can see this [online todo mvc demo](https://codesandbox.io/s/todoapp-react-concent-fvgvc)

## Dependency collection & Exact Update
The key is coming, in fact every render period component is will consume different state, but how should let react know trigger re-render or not?

`Concent` Component in will collect dependency in every render period, let's show what and how it happened one step by one step.

- step 1
Let's give more field in the hello module state
```js
run({
    hello: {
        state: {
            greeting: 'hello world',
            name: 'concent',
            addr: 'https://github.com/concentjs/concent',
        }
    }
})
```

- step 2

Let's give the component a flag to decide to display name or not.
```js
@register("hello")
class HelloWordComp extends React.Component {
  state = { greeting: "xxxxx", show: true };
  render() {
    console.log('Render HelloWordComp ' + this.props.tag);
    // this.ctx.state === this.state
    const { state, sync, syncBool} = this.ctx;
    return (
      <div>
        <input value={state.greeting} onChange={sync('greeting')} />
        {/* if show is true, input will been render */}
        {state.show ? <input value={state.name} onChange={sync('name')} />:''}
        <button onClick={syncBool('show')}>toggle show</button>
      </div>
    )
  }
}
```
for function component, we write it like below, it looks very very similar to class component render block.
```js
const iState = ()=>{
  console.log('this will only been called in first render');
  return {show: true};
}

function HelloWorldFn(props) {
  console.log('Render HelloWorldFn ' + props.tag);
  const { state, sync, syncBool} = useConcent({module:'hello', state:iState});
  return (
    <div>
      <input value={state.greeting} onChange={sync('greeting')} />
      {/* if show is true, input will been render */}
      {state.show ? <input value={state.name} onChange={sync('name')} />:''}
      <button onClick={syncBool('show')}>toggle show</button>
    </div>
  )
}
```

- step 3

let's initialize the component with different tag
```js
export default function App() {
  return (
    <div>
      <HelloWordComp tag="comp1" />
      <HelloWordComp tag="comp2" />
      <HelloWorldFn tag="fn1" />
      <HelloWorldFn tag="fn2" />
    </div>
  );
}
```

- step 4

let's see effect, we type content in any input box will trigger 4 ins re-render
![](https://github.com/fantasticsoul/assets/blob/master/temp/111.gif?raw=true)

and we toggle the 2 `HelloWorldFn` ins to let it don't display name, that means they lost the dependency of `name` state, and then we input the name in the 2 `HelloWordComp` ins to see what happen in the console.

![](https://github.com/fantasticsoul/assets/blob/master/temp/222.gif?raw=true)

see it? the 2 `HelloWorldFn` ins will not been trigger re-render, because 
`Concent` know they lost the dependency of `name` state, they needn't been      
 triggered re-rendered again!

[edit this example](https://codesandbox.io/s/dev-demo-2-qhxpw)

## Dislike module state and private state merged together?
From above example we see `this.state`(`this.ctx.state`) in class render block and `ctx.state` in function block were merged state, if you don't like this way of forming state, you can choose `connect` params, in fact `connect` allow you pass multi module names, so you can consume multi module state easily also.
```js
@register({connect:['foo', 'bar']})
class HelloComp extends React.Component{
  render(){
    const {foo, bar} = this.ctx.connectedState;
    const {f1, f2, f3} = foo;
    const {b1, b2, b3} = bar;
  }
}

function HelloCompFn(){ 
    const ctx = useConcent({connect:['foo', 'bar']});
    const {foo, bar} = ctx.connectedState;
    const {f1, f2, f3} = foo;
    const {b1, b2, b3} = bar;
    // or write in one sentence
    // const {connectedState:{foo:{f1,f2,f3}}} = use***
}
``` 


## Summary
Use concent in react, share state easily and enjoy exact update，it will help you build high performance react app and just let organize code in react way but make it more elegant.

by the way, let's see the dom hierarchy in react-dev-tool, it is very clear and less, no nested `Provider` or `hoc`.
![dom](https://github.com/fantasticsoul/assets/blob/master/temp/WX20200309-214606.png?raw=true)


## ⚖️Some online comparative examples
* [feature list demo](https://codesandbox.io/s/green-tdd-g2mcr)
* [concent todo mvc](https://codesandbox.io/s/todoapp-react-concent-fvgvc) **vs** [redux&hook todo mvc](https://codesandbox.io/s/github/blacksonic/todoapp-react-hooks)
* [concent calculator](https://codesandbox.io/s/react-calculator-8hvqw) **vs** [traditional hook calculator](https://codesandbox.io/s/react-calculator-84f2m)
* [concent query list](https://codesandbox.io/s/query-react-list-00mkd)& [concent shared query list](https://codesandbox.io/s/query-react-list-shared-state-l3fhb) **vs** [traditional hook query list](https://codesandbox.io/s/elastic-dhawan-qw7m4)