简介

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

  1. useReaction - 初始化函数,请在你的App组件内最开始的中央调用。 (承受一个可选参数来启用devtool,举荐在development模式下启用)
  2. useProvider - 调用此函数来获取Provider,用来包裹你的根节点。(在你的任意子组件中,若调用useProvider,拿到的其实是同一个,所以尽管不报错,但也并无额定的用途)
  3. useModel - 获取给定model实例的 {store, doAction, resetModel} ,详解如下:

    1. store - 此model的store,跟model有着同样的数据结构,但不是model自身,相似于[store] = useState(model)这种关系。
    2. doAction - action的执行器,框架外部会限定它只能批改该model的数据,是模块化思维的次要体现。此执行器(函数)承受3个参数,详解如下:

      1. action - 动作函数,用来解决业务逻辑,能够是一般函数或者sync/Promise 函数,能够返回 对model数据的批改局部(kv构造,k必须是model里定义过的字段名); 或者无返回值(只用来解决逻辑,不批改model数据);或者 返回用 justBack(data)包裹的数据,此数据会原样返回给doAction的调用处,但不批改model,不触发渲染。
      2. payload - 业务里调用doAction时,payload会原样传给 action 函数,
      3. showLoading - 执行此action期间,是否显示loading, 参数为 model or global , model 即此loading标记于此model, global 即此loading标记于全局, 默认空,详情参考 useLoading
      4. 留神: doAction 是 async function, 会返回action 函数返回的数据, 所以调用处能够拿到 action 函数的返回值。
      5. 留神: doAction是队列模式, 每次 doAction 都会进入队列, 所以多个doAction 是一个接一个执行的。
    3. resetModel - model数据的重置器,调用它会将model对应的store置回最后model定义时的初始数据。
  4. useLoading - 获取loding状态(true/false,只能获取,设置loading是在doAction时候传参数给showLoading),能够承受一个参数model,即取此model的loading状态,不传则取的是全局的loading状态,参看 doAction(someAction, payload, 'model' | 'global' | true)
  5. 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:

  1. 在App组件内最开始地位,调用useReaction 来初始化框架。若要启用devtool,就useReaction(true)

    export const App: React.FC = () => {    /**init use-reaction */    useReaction()    ...
  2. 紧接着,调用 useProvider() 拿到Provider,用来包裹你的根结点:

    export const App: React.FC = () => {        /**init use-reaction */        useReaction()        /**obtain Provider */        const Provider = useProvider()        /**render */        return <Provider>                  <GlobalLoading>                     ...ChildNodes                  </GlobalLoading>                </Provider>    }
  3. 在某处,定义一个常量来保留你的一个业务模块的初始数据:

       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里预约义过的字段!!      }  }
  4. 业务里调用 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.