前言
随着CcFragment
支持hook
了,私底下有小伙伴问我,在 什么 场景下使用hook
,才能体现出hook
的精髓,以及什么时候支持useStore
和useReducer
。
这里我分开回答一下,解开小伙伴的疑惑:
- 1 什么时候使用
hook
?
我们知道
hook
仅限与在CcFragment
内部使用,那么这里先就先解释一下CcFragment
的出现缘由,当你定义了不少store
和reducer
,在此基础上写了不少cc class
业务组件了,随着功能的迭代,你将要实现的组件需要跨多个模块消费数据,当组件本身交互逻辑和渲染逻辑都复杂的时候,我们可以使用register
或者connect
去装饰一个新的类来达到此目的。
但是组件本身的渲染逻辑不复杂而且很轻量的时候,没有必要去抽象一个
class
来完成此组件,CcFragment
的出现让你可以快速实现类似的组件。
CcFragment
标签通过提供connect
属性让你能够共享多个模块的state
,但是如果用户在CcFragment
里需要保管和操作自己的一些localState
时,看起来就没法了,难道又要将CcFragment
的逻辑写为cc class
组件吗?hook
的出现本质上和react hook
是一个目的,都是为了解决localState
的管理问题,目前CcFragment
里可以使用hook
的两个函数,分别是useState
和useEffect
,使用的效果和react hook
是完全一样的。
useState
返回一个值和setter
组成的元组。useEffect
让你执行副作用,如果不传递第二位参数,useEffect
在每一次CcFragment
渲染完毕都会执行,如果传递第二位参数为空数组,useEffect
仅仅是在CcFragment
挂载完毕执行,如果传递第二位参数为包含元素的数组,useEffect
是否需要执行取决于数组里的元素是否发生了变化,当然useEffect
返回的函数是在CcFragment
卸载时会被执行。
- 2 什么时候支持
useStore
和useReducer
?
其实这个问题是因为小伙伴没有彻底理解
CcFragment
才会有此疑问,上面我们提到了:CcFragment
标签通过提供connect
属性让你能够共享多个模块的state
,理所当然的CcFragment
也提供对应的函数让你直接复用现有的reducer
函数,实际上CcFragment
提供的内置函数是和cc class
完全一致的,所以CcFragment
同样能给你和cc class
一样的使用体验,下面这个例子展示了CcFragment
提供给你的函数,所以聪明的你是不是醒悟过来,useStore
和useReducer
在CcFragment
里已经是多余的存在了^_^
'foo/*'}} render={({hook,propState,dispatch,emit,emitIndentity,invoke,effect,xeffect})=>{
//your logic code here
render see the method above?
}}/>
复制代码
实现Counter
目标
为了进一步解释CcFragment
和hook
,我们来用cc
完成一个有意思的counter
示例,让大家进一步了解,为什么cc
宣传了这样一句话:让你书写优雅的react
代码。
需求
- 基于
cc class
实现一个组件名为ClazzCounter
。 - 基于
CcFragment
实现一个组件名为FnCounter
。 ClazzCounter
对数操作增加,并存储到counter
这个模块的state
里。FnCounter
对数的操作维护在自己实例内部名为localCount
,当localCount
为10的整数倍的时候,同步到counter
里。FnCounter
实现一个自增按钮,点击一次后,自动对counter
模块的state
里的count
值随机增加1~10以内的数,加10次,每增加一次,暂停500ms。FnCounter
的实例卸载时使用alert
弹一句提示语unmount FnCounter
。
store和reducer定义
我们使用cc.startup
函数启动cc
,注入store
和reducer
,reducer
里包含一个自增、自减和随机自增函数
function sleep(ms=600){
return new Promise(resolve=>setTimeout(resolve,ms));
}
function ran(max=10){
return Math.floor(Math.random()*10);
}
startup({
isModuleMode:true,// startup in module mode
store:{
counter:{
count:0,
}
},
reducer:{
counter:{
inc({moduleState, payload:count}){
let toAdd = 0;
if(count!==undefined)toAdd = count ;
else toAdd = 1;
return {count: moduleState.count + toAdd};
},
dec({moduleState, payload:count}){
return {count:moduleState.count-1};
},
async randomInc({dispatch}){
for(let i=0;i<10;i++){
await dispatch('inc', ran());
await sleep();
}
alert('randomInc finished');
}
}
},
middlewares:[
(context, next)=>{console.log(context);next()}
]
});
复制代码
ClassCounter定义
因为cc
会自动注入state,我们这里就不写constructor
了,设定ClazzCounter
的ccClassKey
为Counter
,在react dom tree
上将会与
的方式展示,设定ClazzCounter
属于counter
模块,共享counter
的所有key
的状态变化。
@register('Counter', {module:'counter', sharedStateKeys:'*'})
class ClazzCounter extends React.Component {
inc = ()=>{
this.$$dispatch('inc');
}
render(){
const {count} = this.state;
return (
'1px solid lightgrey',padding:'9px'}}>
count: {count}
);
}
}
复制代码
FnCounter定义
把FnCounter定义为一个function component
,
- 内部使用
CcFragment
,连接上counter
模块的所有数据, - 使用
useState
返回的setter
函数包装一个mySetCount
函数,完成当localCount
为10的整数倍的时候,同步到counter
里这个功能 - 使用
useState
返回一个函数,完成需求:FnCounter
的实例卸载时使用alert
弹一句提示语unmount FnCounter
- 定义一个
的
onClick
事件调用dispatch('counter/randomInc')
,触发随机增加的需求
function FnCounter({label}){
function FnCounter({label}){
return (
'counter/*':''}} render={({propState, hook, dispatch})=>{
const [count, setCount] = hook.useState(0);
const mySetCount = (count)=>{
setCount(count);
if(count%10===0){
dispatch('counter/inc', count);
}
};
hook.useEffect(()=>()=>alert('unmount FnCounter'));
return (
'1px solid blue',padding:'9px',marginTop:'12px'}}>
{label}
counter count:{propState.counter.count}
local count: {count}
);
}}/>
);
}
复制代码
渲染它们,看看效果吧
我们可以的多放几个,看看它们之间的数据同步效果,以及证明FnCounter
的实例是各自维护自己的localCount
值哦。
class App extends React.Component{
render(){
return(
"FnCounter1"/>
"FnCounter2"/>
);
}
}
ReactDOM.render( , document.getElementById('root'));
复制代码
总结
CcFragment
结合hook
,能够让你更加从容的复用已有的代码,以及更优雅的写function component
,组合hook
函数可以玩出更多的新花样,等待你去发现cc
的更多的react
有趣写法。
- 本次示例代码在这里
- 欢迎了解并star,成为cc的种子用户
- 在线示例点我,包含cc class 定义,CcFragmet,hook等
- cc版本ant-design-pro
- 基础入门项目
- runjs录像教程