简介
react app 状态治理(数据管理)始终是我的项目开发中必不可少的环节。小型我的项目在各自组件中应用 useState / setSate 即可。但略微简单一点的我的项目,一遍都会思考引入状态治理框架,如 redux, mobx, dva 等等。
对于简单的我的项目,我一贯推崇应用模块化的思维来治理数据。去年我曾公布过一个基于 redux 的二次封装 package,用于模块化的治理业务数据,有趣味的能够考古参考 module-reaction.
但当初 React Hook 模式的愈发成熟和风行,促使社区造成一股去 redux 风潮。咱们项目组的同学也埋怨说 redux 太重了,于是,我用空闲工夫,基于 hook 公布了这个新包:use-reaction , 并装备了繁难了的 chrome devtool,新鲜出炉,欢送品鉴!
use-reaction
后面说了,这是一个基于 hook 的模块化数据管理框架,体积极小,只有约 100 行代码!
api 和应用形式也尽量简化。
repo
github 仓库:Repo, 记得来给加个 star 哦!!!
chrome-dev-tools
chrome 插件,用户查看模块数据,查看 action 历史,查看 action 扭转的数据 等等,
- 装置:用法是下载之后,关上你的 chrome,关上【设置】->【治理扩大程序】,而后把下载好的 crx 文件拖到 chrome 窗口内,实现装置。download!
- 应用:首先你的 React 我的项目中,调用 useReaction 办法时,传入参数 true 以启用 devtool
- 而后,chrome 关上调试面板(按 f12),你会看到‘Elements,Console,Sources,Network…’ 等一排工具,持续往后面找,找到 UseReaction,点击关上。
(放弃 chrome 调试面板开着)刷新你的 app 页面,或者在你的 app 里触发一个 action,那么 devtool 就能够 track 到你利用中的数据了。
基于平安思考,UseReaction devtool 只能 track 你的利用数据,并不能反过来批改你的 app 内的数据。
ps: devtool 的源码也在 github Repo 当中, 有趣味能够自行查看。再次求 star.
install
npm i use-reaction
apis
useReaction
– 初始化函数,请在你的 App 组件内最开始的中央调用。(承受一个可选参数来启用 devtool, 举荐在 development 模式下启用)useProvider
– 调用此函数来获取 Provider, 用来包裹你的根节点。(在你的任意子组件中,若调用 useProvider,拿到的其实是同一个,所以尽管不报错,但也并无额定的用途)-
useModel
– 获取给定 model 实例的 {store, doAction, resetModel},详解如下:store
– 此 model 的 store, 跟 model 有着同样的数据结构,但不是 model 自身,相似于 [store] = useState(model) 这种关系。-
doAction
– action 的执行器,框架外部会限定它只能批改该 model 的数据,是模块化思维的次要体现。此执行器(函数)承受 3 个参数,详解如下:- action – 动作函数, 用来解决业务逻辑,能够是一般函数或者 sync/Promise 函数,能够返回 对 model 数据的批改局部(kv 构造,k 必须是 model 里定义过的字段名); 或者无返回值(只用来解决逻辑,不批改 model 数据);或者 返回用
justBack(data)
包裹的数据,此数据会原样返回给 doAction 的调用处,但不批改 model,不触发渲染。 - payload – 业务里调用 doAction 时,payload 会原样传给 action 函数,
- showLoading – 执行此 action 期间,是否显示 loading, 参数为 model or global ,
model
即此 loading 标记于此 model,global
即此 loading 标记于全局, 默认空,详情参考useLoading
- 留神: doAction 是 async function, 会返回action 函数返回的数据, 所以调用处能够拿到 action 函数的返回值。
- 留神: doAction 是队列模式,每次 doAction 都会进入队列, 所以多个doAction 是一个接一个执行的。
- action – 动作函数, 用来解决业务逻辑,能够是一般函数或者 sync/Promise 函数,能够返回 对 model 数据的批改局部(kv 构造,k 必须是 model 里定义过的字段名); 或者无返回值(只用来解决逻辑,不批改 model 数据);或者 返回用
resetModel
– model 数据的重置器,调用它会将 model 对应的 store 置回最后 model 定义时的初始数据。
useLoading
– 获取 loding 状态(true/false,只能获取,设置 loading 是在 doAction 时候传参数给 showLoading), 能够承受一个参数 model, 即取此 model 的 loading 状态,不传则取的是全局的 loading 状态,参看doAction(someAction, payload, 'model' | 'global' | true)
-
justBack
– 有时候,你的 action 函数可能只想偷偷摸摸干点啥事,并把后果通知调用方,同时又不想轰动管理者,那就用这个吧!在你 action 里返回数据的时候,用 justBack 包裹你的数据,就能只是把数据返回给调用处,但不批改 model store 也不触发渲染:export const actionJustBackData: Action<typeof model_b> = async function({payload}) { ... do process task ... return justBack('hello' + payload) }
How to use
实例解说 4 个简略步骤来应用 use-reaction:
-
在 App 组件内最开始地位,调用useReaction 来初始化框架。若要启用 devtool,就useReaction(true)
export const App: React.FC = () => { /**init use-reaction */ useReaction() ...
-
紧接着,调用 useProvider() 拿到 Provider, 用来包裹你的根结点:
export const App: React.FC = () => { /**init use-reaction */ useReaction() /**obtain Provider */ const Provider = useProvider() /**render */ return <Provider> <GlobalLoading> ...ChildNodes </GlobalLoading> </Provider> }
-
在某处,定义一个常量来保留你的一个业务模块的初始数据:
export const model_a: ModelA = { a: 1, aa: {aa: 1}, }
同时,要想批改模块数据,须要通过 action,所以,再定义若干 action 吧,
列如:export const actionTestA: Action<ModelA> = ({payload, store}) => { // 下一行会报错, 因为 action 中不能间接批改 store 的数据,而是应该以返回值的模式批改模块数据 // store.a = 6 // 只返回批改的局部,无需 {...store,a: xxx} 这样的全量模式!return { a: store.a + payload, sth: 'hello world' // 这一行’hello world' 尽管返回进来了,但会被框架疏忽,因为只容许批改 model 里预约义过的字段!!} }
-
业务里调用 useModel(model_a) 来取 model store,以及执行 action 以下是一个残缺的应用实例代码:
export const App: React.FC = () => { /**init use-reaction */ useReaction(true) /**obtain Provider */ const Provider = useProvider() /**render */ return <Provider> <GlobalLoading> <SubPageA /> <SubPageB> <CompC /> </SubPageB> </GlobalLoading> <CompC /> </Provider> } function GlobalLoading(props: KV) {const loading = useLoading() console.log('loading:', loading) return < Spin spinning={loading} > {props.children} </Spin > } //--------examples of how to use--------- function SubPageA(props?: KV) {const { store, doAction} = useModel(model_a) const {store: storeB, doAction: doActionB} = useModel(model_b) const onfinish = async (values: any) => {console.log('values', values) await doAction(actionTestA, 2, 'global') console.log('hello hello') } return ( <div className="page page-a"> <h3>page A</h3> <div> value 'A' is {store.a} </div> <div> value 'B' is {storeB.b} </div> <Form onFinish={onfinish}> <Form.Item label="email" name="email"><Input /></Form.Item> <Form.Item label="password" name="password"><Password /></Form.Item> <Button htmlType="submit">increase A with global loading</Button> </Form> <button onClick={async e => {const backed = await doActionB(actionJustBackData, ',world:' + Date.now()) alert(backed) }}>just back data</button> </div> ) } function SubPageB(props: KV) {const { store, doAction} = useModel(model_b) const loading = useLoading(model_b) console.log('model render loading', loading) return (<Spin spinning={loading}> <div className="page page-b"> <h3>page B</h3> <div> value B is {store.b} </div> <button onClick={e => {doAction(actionTestB, 'do action with loading', 'model') }}>Increse B with model loading</button> <h6>see my child compenent below:</h6> {props.children} </div> </Spin> ) } function CompC() {const { store, resetModel, doAction} = useModel(model_a) const {store: storeB, resetModel: resetModelB} = useModel(model_b) return <div className="comp"> <p>the values in model_a:</p> <ul> <li><span>Value A:</span><span>{store.a}</span></li> <li><span>Value AA:</span><span>{store.aa.aa}</span></li> </ul> <button onClick={resetModel}>reset model_a</button> <hr /> <p>the values in model_b:</p> <ul> <li><span>Value B:</span><span>{storeB.b}</span></li> </ul> <hr /> <button onClick={resetModelB}>reset model_b</button> <button onClick={e => doAction(actionTestA, null, 'global')}> do loading</button> </div> }
很多细节,参考 example
欢送 star, issue.