更简约易用的 react 数据流 nearly-react

Nearly

一个简约, 壮大的数据流框架; Github

装置

npm install --save nearly-react

特征

《更简约易用的 react 数据流 nearly-react》

上图为 flux 架构图, Nearly 参考自 flux, 在其基础上做了以下简化和革新:

功能上:

  • 集成 Promise, 我们不再须要多写一个 componentDidMount 要领去异步猎取数据, 更多状况下, 我们将运用 stateless component 让代码越发简约;

  • Store 的运用越发天真, Store 的单实例和多实例运用能很奇妙地完成跨组件通讯和通用组件掌握逻辑的复用;

比拟 flux:

  • API 越发简约, 在营业中平常只会用到 connectdispatch 要领;

  • 对状况举行集合治理, 写法与原始的 React 类似, 进修和迁徙成本低;

  • 更轻量, min 后只需 6K;

运用示例

import React from 'react';
import { render } from 'react-dom';
import {connect, dispatch, registerStore} from 'nearly-react';

registerStore('counter', {
    // 必需完成 init 要领, 它将被隐式挪用, 作用是初始化 state
    init() {
        return {
            count: 0
        };
    },

   add(getState, step) {
       return {
           count: getState().count + step
       };
   }
};

let incr = () => dispatch('counter::add', 1);
let decr = () => dispatch('counter::add', -1);

function Counter(props) {
    return (
        <div>
            <button onClick={incr}> - </button>
            <span>{props.count}</span>
            <button onClick={decr}> + </button>
        </div>
    )
}

let HocCounter = connect('counter', Counter);

render(
    <HocCounter />,
    document.getElementById('root')
)

API

registerStore(storeName, dispatcherSet)

该要领将注册一个 Store, 须要注重的是该要领必需先 connect 实行, 例:

registerStore('customStore', {
    // 必需完成 init 要领
    init() {
        return {sum: 0};
    },
    add(getState, num) {
        return {sum: getState().sum + num};
    }
});

Dispatcher functions(getState, …args)

registerStore 接收的第二个参数里的要领即 Dispatcher functions;
Dispatcher function 的第一个参数为 getState 要领, 该要领返回的永远是当前最新的 state, 其他参数为 dispatch 要领所传的参数;

关于 Dispatcher function 的返回值:

  • 为一般对象时, 返回值直接 merge 进旧 state;

  • Promise 时, 取 Promise.prototype.then 要领里的参数 merge 进旧 state;

  • null 时, 不 merge, 不触发 render;

例:

registerStore('counter', {
    // 必需完成 init 要领, init 中也能够运用 Promise
    init() {
        return fetch('./test.json').then(res => res.json());
    },
    
    add(getState, step) {
        return {
            count: getState().count + step
        };
    },
   
   // 异步增添
    addAsync(getState, step) {
        return new Promise(resolve => {        
            setTimeout(() => {
                // getState 要领返回的永远是最新的 state
                let count = getState().count + step;
                resolve({count})
            }, 1000);
        });
    },

    // 不触发衬着
    nothing(getState, step) {
        return null;
    }
};

dispatch(action, …args)

默许设置的 action 花样为 ${storeName}::${function},

dispatch 会依据 action 映照到响应的 Dispatcher function, 并将 args 作为参数传入 Dispatcher function, 将其返回的结果提交给 Store, 由 Store 触发组件更新;

connect(storeName, Component [, PlaceHolder, isPure])

该要领会依据 storeName 取得 Store, 再将 Store, ComponentPlaceHolder 组合, 返回一个高阶组件;

个中, PlaceHolder 为默许展现组件 (可选), 当且仅当 init 返回 Promise 时有用, 在 Component 被插进去 dom 之前, 组合后的高阶组件会先展现 PlaceHolder 组件, 可用于完成 loading 之类的结果;

但组件过大时, 能够经由过程设置 isPure 为 true 来进步机能, 当设置 isPure 为 true 时, 只需 dispatch 要领能触发组件的 render, 我置信这比经由过程在 shouldComponentUpdate 里写 shallowEqual 要有用很多;

也能够经由过程下面的 configure 设置默许的 isPure 为 true;

进阶运用

dispatcher(action, …args)

dispatch 的高阶函数; 例:

dispatch('counter::add', 1);
等同于: dispatcher('counter::add')(1);

dispatch('test::testAdd', 1, 2, 3, 4);
等同于: dispatcher('test::testAdd', 1, 2)(3, 4);

configure(option)

运用 nearly 举行开辟, 我们须要斟酌 storeName 反复的状况, 我引荐经由过程将 storeName 映照文件途径的体式格局来防止;

nearly 供应了两个可供设置的要领: beforeConnectbeforeDispatch;

  • beforeConnect 会在 connect 要领被挪用之前挪用, 接收的参数为传入 connect 要领的 storeName; 我们能够用它去加载对应的 JS 文件, 并注册 Store;

  • beforeDispatch 会在 dispatch 要领被挪用之前挪用, 接收的参数为传入 dispatch 要领的 action;

默许设置以下:

import {registerStore, getStore} from './store';

let config = {
    // 默许的 isPure
    defaultPure: false,

    // 默许不开启自动注册 Store
    beforeConnect(storeName) {
        // let store = getStore(storeName);

        // if (!store) {
        //    let realName = storeName.split('#')[0];
        //    registerStore(storeName, require(`./actions/${realName}.js`));
        // }
    },

    beforeDispatch(action) {
        let [storeName, dispatcherName] = action.split('::');

        let store = getStore(storeName);
        if (!store) {
            throw Error(`store '${storeName}' does not exist`);
        }

        let dispatcher = store.dispatchers[dispatcherName];
        if (!dispatcher) {
            throw Error(`the module does not export function ${dispatcherName}`);
        }

        return {store, dispatcher};        
    }
}

运用示例:

import {configure, getStore, registerStore} from 'nearly-react';

configure({
    beforeConnect(storeName) {
        // 设置 beforeConnect 要领, 自动注册 Store
        // 当 store 不存在时
        // 自动去 actions 目录下加载 JS 模块, 并注册 Store
        let store = getStore(storeName);

        if (!store) {
            let realName = storeName.split('#')[0];
            registerStore(storeName, require(`./actions/${realName}.js`));
        }
    }
});

统一 Store 单实例运用

在营业中我们常常须要跨组件通讯, 或许组件间同享数据;

运用 Nearly 我们能很轻易地将两个差别的组件绑定雷同的 Store, 只需传入 connectstoreName 是雷同的即可;
例: 简朴的输入同步显现


registerStore('vm', {
    // 必需完成 init 要领, 它将被隐式挪用, 作用是初始化 state
    init() {
        return {
            value: ''
        };
    },

   change(getState, value) {
       return {
           return { value };
       };
   }
};

// /components/Input.js
let change = e => dispatch('vm::change', e.target.value);

function Input(props) {
    return <input value={props.value} onChange={change} />
}
export default connect(Input, 'vm');


// /components/Text.js
function Text(props) {
    return <p>{props.value}</p>
}
export default connect(Text, 'vm');

详见示例: One-store

统一 Store 多实例运用

我们开辟通用组件时会须要给统一组件绑定统一 store 的差别实例以复用; 能够经由过程给 storeName 加上 #id 来辨别差别 Store;

// Dialog.js
export default function Dialog (props){
    return <div>{props.content}</div>
}

let DialogA = connect(Dialog, 'dialog#a');
let DialogB = connect(Dialog, 'dialog#b');

// 封闭弹窗 A
dispatch('dialog#a::close');
// 封闭弹窗 B
dispatch('dialog#b::close');

注重, 当在组件内部运用 dispatch 时, 能够经由过程 props._storeName 来肯定 storeName;

详见示例: Dialog

示例

Tips

  1. nearly-config.js 必需在营业逻辑之前加载;

  2. 虽然有 registerStore API, 不过作者照样引荐运用 connect 来隐式注册 Store, 由于 connect 经由过程 storeName 映照文件的体式格局来注册 Store, 在确保唯一性的同时更轻易保护和 debug;

  3. 在 Nearly 中对 Promise 的推断是不正确的 (只需有 then 要领均认为是 Promise 实例) , 一方面是由于 Nearly 中只运用了 then 要领, 另一方面是为了兼容 jQuery.Deferred 等类库;

  4. 迎接提 issue 或是 pr;

    原文作者:罗俊彬
    原文地址: https://segmentfault.com/a/1190000009887458
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞