幻魂
Posted on March 9, 2020
Star 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
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.
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
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
import { run } from 'concent';
run({
hello: {
state: {greeting: 'hello world'}
}
})
- step 2, register the target component to concent component
// 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
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
and let us see the effect
you can also edit the demo here
Hate boring setState?
if you hate writing many setState
, you can use sync
series api.
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
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
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.
@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.
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
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
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.
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!
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.
@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
.
⚖️Some online comparative examples
Posted on March 9, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.