初体验
以下代码片段将展示 concent slogan 里的特性,方便你快速了解它与其它状态管理的巨大差异。
0 入侵
使用 concent,可以在你原始 react 组件一行代码不改动的情况下,直接接入状态管理。
❤️0入侵
启动concent,载入模块配置
import { run } from 'concent';
run({
counter: {
//定义counter模块
state: { count: 1 }, //state定义,必需
},
});
定义类组件
import { register } from 'concent';
//注册成为Concent Class组件,指定其属于counter模块
@register('counter')
class CounterComp extends Component {
render() {
const { count } = this.state;
return (
<div>
count: {count}
<button onClick={() => this.setState({ count: count + 1 })}>inc</button>
<button onClick={() => this.setState({ count: count - 1 })}>dec</button>
</div>
);
}
}
代码0改动接入concent
渐进式
得益于 concent 灵活的 api 设计,允许你逐步的解耦渲染逻辑与业务逻辑,让组件保持只输出视图的最小职责,彻底提高代码的可阅读性与可维护性。
🌟渐进式
新增reducer定义
import { run } from 'concent';
run({
counter: {
//定义counter模块
state: { count: 1 }, //state定义,必需
reducer: {
//reducer函数定义,可选
inc(payload, moduleState) {
return { count: moduleState.count + 1 };
},
dec(payload, moduleState) {
return { count: moduleState.count - 1 };
},
},
},
});
通过dispatch修改状态
import { register } from 'concent';
//注册成为Concent Class组件,指定其属于counter模块
@register('counter')
class CounterComp extends Component {
render() {
//ctx是concent为所有组件注入的上下文对象,携带为react组件提供的各种新特性api
return (
<div>
count: {this.state.count}
<button onClick={() => this.ctx.dispatch('inc')}>inc</button>
<button onClick={() => this.ctx.dispatch('dec')}>dec</button>
</div>
);
}
}
逐步解耦业务逻辑与ui渲染
对于 concent 来说,Hoc class, renderProps, hook 三种组件写法是高度统一的,对于 concent 来说,它们只是渲染的载体,注入的实例上下文对象
ctx
才是 concent 的灵魂,让你可以在它们 3 种形态之间丝滑的任意切换,所以你大可以不用担心 class 组件与 function 组件怎么共享代码以及怎么共享业务逻辑。
定义为RenderProps组件
import { registerDumb } from 'concent';
const CounterRenderPropsComp = registerDumb('counter')((ctx) => {
return (
<div>
count: {ctx.state.count}
<button onClick={() => ctx.dispatch('inc')}>inc</button>
<button onClick={() => ctx.dispatch('dec')}>dec</button>
</div>
);
});
定义为Hook组件
import { useConcent } from 'concent';
function CounterHookComp() {
const ctx = useConcent('counter');
return (
<div>
count: {ctx.state.count}
<button onClick={() => ctx.dispatch('inc')}>inc</button>
<button onClick={() => ctx.dispatch('dec')}>dec</button>
</div>
);
}
高性能
基于依赖标记、引用收集和状态分发原理工作,内置
renderKey
、lazyDispatch
、delayBroadcast
等特性,从状态提交那一刻,concent 就精确的知道怎么样缩小渲染范围、减少渲染次数、降低渲染频率,保证大型 react 工程的极致的渲染效率。
⚡️高性能
renderKey
缩小长列表渲染范围
lazyDispatch
减小渲染次数
delayBroadcast
降低渲染频率
增强 react
有了实例上线文
ctx
,配合setup
特性,concent 可以非常从容自然的增强 react 组件功能,离开这些功能你依然能够开发 react 应用,但用上这些功能能你拥抱更好的开发范式,全面提升大型 react 工程的编码优化度和架构体验。
setup
是针对组件实例提供的一个非常重要的特性,在类组件和函数组件里都能够被使用,它会在组件首次渲染之前会被触发执行一次,其返回结果收集在ctx.settings
里,之后便不会再被执行,所以可以在其中定义实例computed
、实例watch
、实例effect
等钩子函数,同时也可以自定义其他的业务逻辑函数并返回,方便组件使用。 利用 setup 只执行一次的特性,可以让函数组件省去重复渲染期间,重复生成临时闭包函数,同时需要手动调用useCallback
等辅助优化函数
🛠增强react
实例computed、watch、effect、emit&on etc...
import { run, useConcent } from 'concent';
run({
counter: {
//定义counter模块
state: {
//【必需】定义state
count: 1,
msg: '',
},
reducer: {
//【可选】定义reducer,解耦业务逻辑与ui渲染
inc(num, moduleState, actionCtx) {
return { count: moduleState.count + num };
},
changeMsg(msg, moduleState, actionCtx) {
return { msg };
},
async complexInc(num, moduleState, actionCtx) {
await api.track('inc');
actionCtx.dispatch('inc', inc);
actionCtx.dispatch('changeMsg', 'call complexInc');
},
},
computed: {
//【可选】定义模块计算函数
count: (count) => count * 2,
// 收集到此函数的依赖为 ['count', 'msg']
autoDep: (newState, oldState, fnCtx) => {
const { count, msg } = newState;
const { changed } = fnCtx;
return `${changed.join(',')} changed, count ${count}, msg ${msg}`;
},
manualDep: {
fn: (newState, oldState, fnCtx) => {
const { changed } = fnCtx;
return `${changed.join(',')} changed`;
},
// 人工标记依赖为 ['count', 'msg'],不管函数体有没有用到这些值,只要它们改变了就触发此计算函数
depKeys: ['count', 'msg'],
},
},
watch: {
//【可选】定义模块观察函数
count: (count, oldVal) => console.log(`old ${oldVal} new ${count}`),
anyOneChange: {
fn: (newState, oldState, fnCtx) => {
console.log(`${changed.join(',')} changed`);
},
depKeys: ['count', 'msg'],
},
},
init: async () => {
//【可选】异步的初始化模块状态
const data = await api.fetchData();
return data;
},
},
});
const setup = (ctx) => {
//count变化时的副作用函数,第二位参数可以传递多个值,表示任意一个发生变化都将触发此副作用
ctx.effect(() => {
console.log('count changed');
}, ['count']);
//每一轮渲染都会执行
ctx.effect(() => {
console.log('trigger every render');
});
//仅首次渲染执行的副作用函数
ctx.effect(() => {
console.log('trigger only first render');
}, []);
//定义实例computed,因每个实例都可能会触发,优先考虑模块computed
ctx.computed('count', ({ count }, oldState, fnCtx) => {
return count * 2;
});
//定义实例watch,区别于effect,执行时机是在组件渲染之前
//因每个实例都可能会触发,优先考虑模块watch
ctx.watch('count', ({ count }, oldState, fnCtx) => {
//发射事件
ctx.emit('countChanged', newVal);
api.track(`count changed to ${newVal}`);
});
//定义事件监听,concent会在实例销毁后自动将其off掉
ctx.on('changeCount', (count) => {
ctx.setState({ count });
});
return {
inc: () => ctx.dispatch('inc', ctx.state.count + 1),
complexInc: () => ctx.dispatch('complexInc', ctx.state.count + 1),
};
};
const iState = { privCount: 0 }; //此state相当于组件的私有状态
function HookFnComp() {
const {
state: { privCount, count, msg },
settings: { inc, dec, incStoreCount, decStoreCount },
} = useConcent({ module: 'counter', setup, state: iState });
return (
<div>
count: {count}
msg: {msg}
privCount: {privCount}
<button onClick={inc}>+</button>
<button onClick={complexInc}>+</button>
</div>
);
}