star me if you like concent ^_^
concent
❤️ Build-in dependency collection, a predictable、zero-cost-use、progressive、high performance's react develop framework
review this gif source code or see a full demo
📦Quick start
Make sure you have installed nodejs。
Install concent
Install concent
with npm command.
$ cd cc-app
$ npm i --save concent
or yarn command
$ yarn add concent
Define module
Use run
to define a module.
import { run } from 'concent';
run({
counter: {// declare a moudle named 'counter'
state: { num: 1, numBig: 100 }, // define state
},
// you can also put another module here.
});
Cosume state & change state
Use register
to specify a module for class component, or useConcent
for function component.
it will let concent konw which module current component belong to.
import { register, useConcent } from 'concent';
@register('counter')
class DemoCls extends React.Component{
// now setState can commit state to store
// and broadcast state 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']
const { num } = this.state;
// render logic
}
}
function DemoFn(){
const { state, setState } = useConcent('counter');
const inc = ()=> setState({num: state.num + 1});
// render logic
}
Attention that state
is a proxy object, for helping concent collect every instantce's dep keys in every render period, that makes exact update become true
Initialize component
There is no need to wrap with the root component with a Provider
, you can just initialize the concent component any where you want, here you can view the demo.
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<div>
<ClsComp />
<FnComp />
</div>
</React.StrictMode>,
rootElement
);
Define reducer
If you have many logic code before changing state, we recommend put them to reducer
concent emphasize user should always return a partial state instead of whole state, it will let concent working in best performance mode, so just return what you changed in the reducer function.
run({
counter: {
state: { /** ... */},
reducer: {
inc(payload, moduleState) {
return { num: moduleState.num + 1 };
},
async asyncInc(payload, moduleState) {
await delay();
return { num: moduleState.num + 1 };
}
}
},
});
now you can call reducer funtion in your component instead of setState
// --------- for class component -----------
changeNum = () => this.setState({ num: 10 })
// ===> modify as below (attention that mr is alias of moduleReducer)
changeNum = () => this.ctx.mr.inc(10);// or this.ctx.mr.asynInc(10)
// of course you can call dispatch, but we think moduleReducer is better
//this.ctx.dispatch('inc', 10); // or this.ctx.dispatch('asynInc', 10)
// --------- for function component -----------
const { state, mr } = useConcent("counter");// useConcent returns ref ctx
const changeNum = () => mr.inc(20); // or ctx.mr.asynInc(10)
infact concent allow user change with top api setState
、dispatch
and reducer
.
- with
setState
import { getState, setState } from "concent";
console.log(getState('counter').num);// log: 1
setState('counter', {num:10});// change counter module's num state
console.log(getState('counter').num);// log: 10
- with
dispatch
dispatch
return a promise, so we should wrap the logic withasync
import { getState, dispatch } from "concent";
(async ()=>{
console.log(getState("counter").num);// log 1
await dispatch("counter/inc");// call counter reducer inc method
console.log(getState("counter").num);// log 2
await dispatch("counter/asyncInc");// call counter reducer asyncInc method
console.log(getState("counter").num);// log 3
})()
- with
reducer
infact concent collect all module's reducer to its internalreducer
map to let user call reducer method directly!
import { getState, reducer as ccReducer } from "concent";
(async ()=>{
console.log(getState("counter").num);// log 1
await ccReducer.counter.inc();
console.log(getState("counter").num);// log 2
await ccReducer.counter.asyncInc();
console.log(getState("counter").num);// log 3
})()
Define computed
If you want to compute another state with module state, we recommend put them to computed
run({
counter: {
state: { /** ... */},
reducer: { /** ... */},
computed: {
numx2: ({num})=> num * 2,
numBigx2: ({numBig})=> numBig * 2,
numSumBig: ({num, numBig})=> num + numBig,
}
},
});
// get computed result in funtion component
const { moduleComputed } = useConcent('counter');
// get computed result in class component
const { moduleComputed } = this.ctx;
Attention that when you deconstruct the state for a computed function, you are also declare the dep keys for the function at the same time.
// current function will only been execute when num or numBig changed.
const numSumBig = ({num, numBig})=> num + numBig,
async comoputed is also supported, here see the online demo.
Some advanced Features
Concent allow user write code with its cool features, they are all optional, but once you learned it you will love it.
🎇Enjoy composition api🎊 🎉
with composition api, user can easily separate ui and logic.
view this demo
import { run, useConcent } from "concent";
run();// startup concent
const setup = ctx => {
const { initState, computed, watch, setState, sync } = ctx;
initState({ greeting: 'hello concent' });
computed("reversedGreeting", n => n.greeting.split('').reverse());
watch("greeting", (n, o) => alert(`from ${o.greeting} to ${n.greeting}`));
return {
changeGreeting: (e) => setState({ greeting: e.target.value }),
changeGreeting2: sync('greeting'),
};
};
function HelloConcent(){
const { state, refComputed, settings } = useConcent({ setup });
return (
<>
<h1>{state.greeting}</h1>
<h1>{refComputed.reversedGreeting}</h1>
<input value={state.greeting} onChange={settings.changeGreeting}/>
<input value={state.greeting} onChange={settings.changeGreeting2}/>
</>
);
}
🎆Unified coding of class components and function components
setup
can be used in both class and function component, that means user can easily share logic (even including life cycle logic) between the 2 kinds of component.
// for class
@register({setup})
class ClsComp extends React.Component{...}
// for function
function FnComp(){
useConcent({setup});
}
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。
Use with plugin-loading
Details see here concent-plugin-loading,control all your reducer function's loading status easily。
Top comments (0)