React Native + Flux

来源:互联网 发布:全自动手机降温软件 编辑:程序博客网 时间:2024/05/23 13:42

1、基于React的Flux架构

React 是基于View层的前端库,其核心是使用组件定义界面。React是组件化和状态机的思想,对于组件与组件、组件与数据模型之间的通信,页面的渲染,需要一套机制来组织整个应用的状态。Flux即是这样一种用来构建React的应用程序加架构,最大的特点是单向数据流,分层解耦。比较适用于大型项目。基本框架如下:


flux包括了四个东西:

view: 视图 controller调用action行为函数,实现change回调

action:动作实现action在dispatcher中的注册

dispatcher:派发器调用store中数据更新以及change 提交功能函数

store:数据层实现数据更新以及事件接口回调

      应用共用一个派发器,一般View的数据变化主要有两个方面:来自服务器端的数据和用户交互数据。对于用户交互的数据,单向数据流的主要流程是:用户交互数据在View Controller中将数据交互封装成Action,并把该Action通过dispatch()函数携带actionType加入到在派发器中;派发器通过register()函数,将不同类型的action分发到不同的数据层进行处理(调用数据层中的不同函数),并提交事件(emit);数据层Store需要编写不同的数据处理函数,来进行本地数据存储的更新(一般这个函数的编写与Action对应),Store需要在变动后向View发送"change"事件,因此必须实现事件接口继承EventCommitter.prototype,调用Store.emit()和store.on()实现)。

注:1、view controller 保存状态,转发给子组件,这样保证子组件不含任何状态,从而进行组件复用。

      2、事件接口的实现在Store中,Dispatcher中更新Store,提交change event;Controller view中监听change event,从Store中拉取数据,            传给子组件。

      3、基于React的Demo:https://github.com/xianyulaodi/myFluxTest

2、Flux在RN中的应用

Flux当初是针对React提出的,RN是基于React的衍生品,以下针对RN进行Flux的应用研究。

Demo流程图:

                                      

demo地址:https://github.com/saralalala/flux-RN

3、Flux主流框架Redux(使用React-Redux库

Redux是Flux的一种框架实现,Redux 放弃了 event emitters(事件发送器),转而使用纯 reducer。使用更方便简洁:


(1)发起action:实现action在dispatcher中的注册(把action创建函数传到到dispatch()方法上或者创建被绑定的action函数

 

        1)action创建函数传到到dispatch()方法上
function addTodo(text) { return { type: ADD_TODO, text }}
     dispatch(addTodo(text))
        2)创建一个被绑定的action创建函数:
     const boundAddTodo = (text) => dispatch(addTodo(text));boundAddTodo(text);

 

(2)reducer:纯函数,接收旧的state和action,返回新的state(注意不要修改旧的state值)

 

    (previousState, action) => newState

 

      reducee根据业务逻辑可以有很多个,最终传给Store的只有一个,可以通过combineReduces() 合成reducer,生成一个函数。这个函数根据reducer 的 key 来筛选出 state 中的一部分数据并处理,然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象,用来创建

(3)store: 确定state对象结构

   1)一个应用中只有唯一一个Store;

    2)Store以树的形式保存了整个应用的State,本质上是一个对象。主要职责:

 

  • 维持应用的 state;
  • 提供 getState()方法获取 state;
  • 提供 dispatch(action) 方法更新 state(发布);

  • 通过 subscribe(listener) 注册监听器,订阅Store树的监听;
  •  replaceReducer() 动态替换reducer函数

    3)建议格式:todosById: { id -> todo } 和 todos: array<id

redux数据流程源码分析:

当state数据变化时,会触发所有订阅了该事件的监听器,在dispatch中循环执行监听器回调函数,从而在回调函数中进行相应处理.

1、事件的订阅通过subscribe(listener)执行

2、事件的触发在dispatch(action)中

源码:

(1)store.subscribe()

function subscribe(listener) {

        if (typeof listener !== 'function') {

            throw new Error('Expected listener to be a function.')

        }

        var isSubscribed = true;

        ensureCanMutateNextListeners()

        nextListeners.push(listener);

        //返回接口,正好利用闭包可以保持对相应Listener的访问

        return function unsubscribe() {

            if (!isSubscribed) {

                return

            }

            isSubscribed = false

            ensureCanMutateNextListeners()

            var index = nextListeners.indexOf(listener)

            nextListeners.splice(index, 1)

        }

    }

(2)store.dispatch(action)

/**
     * 唯一改变state的接口
     * 生成nextState同时通知观察者
     * 每次dispatch都会执行state的观察者
     *
     * return action这样设计比较好的一点是方便扩展中间件!!!
     */
    function dispatch(action) {
        /**
         * 错误处理,可以跳过
         */
        if (!isPlainObject(action)) {
            throw new Error(
                'Actions must be plain objects. ' +
                'Use custom middleware for async actions.'
            )
        }
        if (typeof action.type === 'undefined') {
            throw new Error(
                'Actions may not have an undefined "type" property. ' +
                'Have you misspelled a constant?'
            )
        }

        if (isDispatching) {
            throw new Error('Reducers may not dispatch actions.')
        }
        /**
         * 将当前状态和action作为参数传值给reducer,生成下个状态
         */
        try {
            isDispatching = true
            currentState = currentReducer(currentState, action)
        } finally {
            isDispatching = false
        }

        var listeners = currentListeners = nextListeners
        for (var i = 0; i < listeners.length; i++) {
            /**
             *  遍历subscribe的观察者
             */
            listeners[i]()
        }
        return action
    }

(4)在React中的应用:需要React-Redux 提供API(Provider/Connect)

           Provider和Connect是React-Flux提供的接口负责把React Component 和 Redux store 结合起来:

 Provider 把可以将从createStore返回的store放入context中,使子集可以获取到store并进行操作;

<Provider store={store}>{() => <App />}</Provider>

connect的两个可选参数,前两个

  • [mapStateToProps(state, [ownProps]): stateProps]: 第一个可选参数是一个函数,只有指定了这个参数,这个关联(connected)
  • 组件才会监听 Redux Store 的更新,每次更新都会调用 mapStateToProps 这个函数,返回一个字面量对象将会合并到组件的
  •  props 属性。 ownProps 是可选的第二个参数,它是传递给组件的 props,当组件获取到新的 props 时,ownProps 
  • 都会拿到这个值并且执行 mapStateToProps 这个函数。
  • [mapDispatchProps(dispatch, [ownProps]): dispatchProps]: 这个函数用来指定如何传递 dispatch给组件,在这个函数里面
  • 直接 dispatch action creator,返回一个字面量对象将会合并到组件的 props属性,这样关联组件可以直接通过 props 调用
  • 到 action, Redux 提供了一个 bindActionCreators() 辅助函数来简化这种写法。 如果省略这个参数,默认直接把 
  • dispatch 作为 props 传入。ownProps 作用同上。
组件的更新:

在 redux 中,没有与 redux 有直接关联的组件称为木偶组件,不理外面纷纷扰扰,只知道自己拥有了 state 及 具备操作 state 数据的 actions 方法。 
当木偶组件使用 actions 方法,更新了 store.state 的数据时,将会触发 store 中的 subscribe 所注册的函数。而其中一个注册函数,就在 Connect 组件中静默注册了。 

我们把connect返回的函数叫做Connector,它返回的是内部的一个叫Connect的组件,它在包装原有组件的基础上,还在内部监听了Redux的store的变化,

为了让被它包装的组件可以响应store的变化:

trySubscribe() { if (shouldSubscribe && !this.unsubscribe) {this.unsubscribe =this.store.subscribe(::this.handleChange)this.handleChange() }}

handleChange () { this.setState({ storeState: this.store.getState()})}

但是通常,我们connect的是某个Container组件,它并不承载所有App state,然而我们的handler是响应所有state变化的,于是我们需要优化的是:当storeState变化的时候,仅在我们真正依赖那部分state变化时,才重新render相应的React组件,那么什么是我们真正依赖的部分?就是通过mapStateToPropsmapDispatchToProps得到的。

具体优化的方式就是在shouldComponentUpdate中做检查,如果只有在组件自身的props改变,或者mapStateToProps的结果改变,或者是mapDispatchToProps的结果改变时shouldComponentUpdate才会返回true,检查的方式是进行shallowEqual的比较。

// 在 Connect 中 

this . store . subscribe ( this . handleChange . bind ( this ) ) ; 
即当 actions 更改了 state 时,会调用注册函数 handleChange。从而进行 “阿米诺骨牌式” 的函数执行连锁反应。更新了 state,并使用新的数据重新 render 组件。

实际上是为智能组件(传入 connect 的组件)传入新的 props,因为各个子元素是通过引用父级组件的 props,所以将进行一级一级的差异数据更新(PureRenderMixin
),
最终
效果就是页面更新了。 

实际上,这里与简单的发布订阅模式类似。使用 store.subscribe(cb); 来订阅一个回调函数,子组件进行 action 操作 store.state 时进行发布,执行了回调函数。


 通过connect 获得store中的state,并转化成Component的props来使用。(以下为引用Wang Namelos在知乎的回答)

1. React有props和state: props意味着父级分发下来的属性,state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,也就是说数据只能单向向下分发,或者自行内部消化。
理解这个是理解React和Redux的前提。
2. 一般构建的React组件内部可能是一个完整的应用,它自己工作良好,你可以通过属性作为API控制它。但是更多的时候发现React根本无法让两个组件互相交流,使用对方的数据。
然后这时候不通过DOM沟通(也就是React体制内)解决的唯一办法就是提升state,将state放到共有的父组件中来管理,再作为props分发回子组件。
3. 子组件改变父组件state的办法只能是通过onClick触发父组件声明好的回调,也就是父组件提前声明好函数或方法作为契约描述自己的state将如何变化,再将它同样作为属性交给子组件使用。
这样就出现了一个模式:数据总是单向从顶层向下分发的,但是只有子组件回调在概念上可以回到state顶层影响数据。这样state一定程度上是响应式的。
4. 为了面临所有可能的扩展问题,最容易想到的办法就是把所有state集中放到所有组件顶层,然后分发给所有组件。
5. 为了有更好的state管理,就需要一个库来作为更专业的顶层state分发给所有React应用,这就是Redux。让我们回来看看重现上面结构的需求:
a. 需要回调通知state (等同于回调参数) -> action
b. 需要根据回调处理 (等同于父级方法) -> reducer
c. 需要state (等同于总状态) -> store
对Redux来说只有这三个要素:
a. action是纯声明式的数据结构,只提供事件的所有要素,不提供逻辑。
b. reducer是一个匹配函数,action的发送是全局的:所有的reducer都可以捕捉到并匹配与自己相关与否,相关就拿走action中的要素进行逻辑处理,修改store中的状态,不相关就不对state做处理原样返回。
c. store负责存储状态并可以被react api回调,发布action.
当然一般不会直接把两个库拿来用,还有一个binding叫react-redux, 提供一个Provider和connect。很多人其实看懂了redux卡在这里。
a. Provider是一个普通组件,可以作为顶层app的分发点,它只需要store属性就可以了。它会将state分发给所有被connect的组件,不管它在哪里,被嵌套多少层。
b. connect是真正的重点,它是一个科里化函数,意思是先接受两个参数(数据绑定mapStateToProps和事件绑定mapDispatchToProps),再接受一个参数(将要绑定的组件本身):
mapStateToProps:构建好Redux系统的时候,它会被自动初始化,但是你的React组件并不知道它的存在,因此你需要分拣出你需要的Redux状态,所以你需要绑定一个函数,它的参数是state,简单返回你关心的几个值。
mapDispatchToProps:声明好的action作为回调,也可以被注入到组件里,就是通过这个函数,它的参数是dispatch,通过redux的辅助方法bindActionCreator绑定所有action以及参数的dispatch,就可以作为属性在组件里面作为函数简单使用了,不需要手动dispatch。这个mapDispatchToProps是可选的,如果不传这个参数redux会简单把dispatch作为属性注入给组件,可以手动当做store.dispatch使用。这也是为什么要科里化的原因。

demo地址:demo地址:https://github.com/saralalala/redux-RN

5、参考文章:

1)http://www.ruanyifeng.com/blog/2015/03/react.html;廖雪峰React

2)http://www.cnblogs.com/xianyulaodi/p/5358315.html;Flux学习笔记

3)http://cn.redux.js.org/docs/introduction/ThreePrinciples.html ; Redux中文官网

4)http://www.jianshu.com/p/09956d82bca6;Redux解析

5)http://www.zhihu.com/question/41312576?sort=created ;




2 0
原创粉丝点击