use-reaction
TypeScript icon, indicating that this package has built-in type declarations

1.3.3 • Public • Published

use-reaction

react modulized store manager framework based on react hooks. easy to manage your app's states in modulized way.

main features

  • Pure React just react hooks
  • Small Size 100 lines code
  • Small Api easy to learn
  • modulized devide your states to business units so easy to manage and keep it safe from one another
  • chrome-dev-tool use chrome dev tool to view your module store and action history(in dev mode)

repo

here is Repo, If you like this framework, consider give me a star!

chrome-dev-tools

the chrome extention devtool has been released, download!

use devtool to track the actions and models sanpshot.

install

npm i use-reaction

core concept:

  • init:
    • call useReaction to init the framework
    • call useProvider to retrieve the root Wrapper
  • develop:
    • define model instance to present module store's properties for your business.
    • call useModel in your UI components to get the module store data.
    • call doAction to change parts of your module store. once action done. will trigger re-render of UI above

apis

  1. useReaction - the initial function of this framework, accept two optional params,

    • first param is enableDev,you can enableDevtool on development mode, so that you can use chrome-dev-tool to view the module-stores and action history (not recommend to enable dev on production mode)
    • secod param is strict, you can enable strict mode if you want to limit the action's back data only modify model-store's exists properties.

  2. useProvider - return the Provider used to wrap your app's root component


  3. useModel - retrive the {store, doAction, resetModel} of given model instance

    1. store - the store of this model
    2. doAction - the action trigger, accept 3 params, these are:
      • action - the function (normal or PromiseLike), which can return changed part of model's Interface, or return nothing, or return justBack(data) to avoid modify store
      • payload - the paload which will pass to the action function,
      • showLoading - whether showloading when process this action, you can pass param model or global and, model means change loading state for this model-store, global means change loading for global, default undefined, see useLoading
      • loadingTip - optional loading tip text.
      • Note: the doAction is an async function, and will return what the action function's return-data, so you can get the result-data of the action function
      • Note: each doAction call will be serialized into queue, so, if your call doAction multi-times, it will finish one by one!!
      • Note: if error occur during this action. framework will print error message to console, and ignore this action, then try to excute next action in the queue.
    3. doFunction - the trigger to call what you like function, it won't affect model data, but can use the model/global loading.
      • eg. in your logic, you have actionA, and actionB, later, you want to call api to check sth before *actionB, then you can insert a doFunction section, code like:
      async doSth() =>{
          doAction(actionA);
          // execute callApi, and trigger the global loading, without modify model-data
          doFunction(async()=> {
              await callApi(xxx)
          }, 'global');
      
          doAction(actionB);
          ...
      }
    4. resetModel - the trigger for reset the given model to its initial state when it's defined

  4. useLoadingTip- retrive the loading-flag(true/false) and loading-tip of given model-instance, if not provide model, then it will return the global loading flag, this flag and tip will change when call doAction(someAction, payload, 'model' | 'global', loadingTip?) [added since v1.3.0]

  5. useLoading - a simplified alias of useLoadingTip, while this one only retrieve the loading-flag(true/false) of given model-instance, if not provide model, then it will return the global loading flag, this flag will change when call doAction(someAction, payload, 'model' | 'global')


  6. setLoading - NOT recommended to use! you'd better trigger loading by call doAction or doFunction. This function might be usefull where need to mark model or global loading in non-UI section, eg. within fetch or axios call. [added since v1.3.0]


  7. justBack - sometimes, your action don't want to modify the model store, just want to process sth and return the data back to UI level, then you can use this api to wrap your return data, so that the return data of your action won't trigger modify and won't trigger rerender, like this:

    export const actionJustBackData: Action<typeof model_b> = async function({ payload }) {
        ... do process task ...
       return justBack('hello' + payload)
    }

How to use

  1. call useReaction() at the top line of your App Component function to init, like this;

    export const App: React.FC = () => {
        /**init use-reaction */
        useReaction()
        ...
  2. call useProvider() to obtain the root wrapper of your app, like this:

    export const App: React.FC = () => {
            /**init use-reaction */
            useReaction()
    
            /**obtain Provider */
            const Provider = useProvider()
    
            /**render */
            return <Provider>
                      <GlobalLoading>
                         ...ChildNodes
                      </GlobalLoading>
                    </Provider>
        }
  3. define your models as constants, models are sth of key-value object, like this:

       export const model_a: ModelA = {
          NAME: 'model_a', // optional, but it's better have a 'NAME' prop, then devtool can display this specific name.
          a: 1,
          aa: {
              aa: 1
          },
      }

    also, you need to define your action( a function to process sth and return the changed data), like this:

    export const actionTestA: Action<ModelA> = ({ payload, store }) => {
          // the next-line will cause error, b/c in action, the store's prop can't be modified directly
          // store.a = 6
    
          // return changed part
          return {
              a: store.a + payload,
              sth: 'hello world' // Note: if you enabled strict mode, this field will be ignored and won't be added into model_a b/c property 'sth' is not defined in model_a instance !!!
          }
      }

    Note: return empty object {} in your action equals to return nothing, won't trigger re-render.

  4. call useModel(model_a) in your non-root Component to get certain model's store and action-dispatcher, here is the full featured example:

    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, tip} = useLoadingTip(model_b)
        console.log('model render loading', loading)
        return (
            <Spin spinning={loading} tip={tip}>
    
                <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>
    }

more details, see the example, you can clone the repo and run locally to know it better.

Package Sidebar

Install

npm i use-reaction

Weekly Downloads

7

Version

1.3.3

License

MIT

Unpacked Size

26.7 kB

Total Files

4

Last publish

Collaborators

  • swellee