ngrx/store综合介绍,读完马上精通angular中的ngrx/store


像传统数据一样,代表着应用的记录点,你的Store可以被认为是客户端”真实的数据来源” 或 数据库。在设计应用时都遵守一个Store的约定,Store任何时刻的存储快照都将只是呈现应用程序的完整状态。

一个单一的、不可变的状树,只有通过显式定义和调度才能更新。

中心化,不可变状态

ngrx/store综合介绍,读完马上精通angular中的ngrx/store

Reducers

Store应用程序的第二个组成部分是reducers。A2 reducer 是一个 a3纯函数,前一个状态和一个与事件相关联的类型和可选数据(playload)的Action。使用以前的说法是,如果Store被认为是客户端的数据库, 则reducers可以被认为是数据库中数据表。Reducers代表着应用程序的部分或状态片段,应相应的进行结构化和组合。

Reducer 接口2
export interface Reducer<T> {
  (state: T, action: Action): T;
}

A3函数的返回值类型由其输入值的类型决定的。

一个简单的Reducer
export const counter: Reducer<number> = (state: number = 0, action: Action) => {
  switch(action.type) {
    case 'INCREMENT':
        return state + 1;
    case 'DECREMENT':
        return state -1;
    default:
        return state;
  }
}
Actions

Store包含了我们应用程序的state和Reducers的输出部分,但是当状态需要更新时,我们如何与reducers通信呢?这是actions4,在Store应用程序中,所有导致状态更新的用户交互都必须以actions的形式表示。所有与用户相关的事件都被分派为action,经过Store的4个action通道,然后输出一个新的状态表示。每次调度一个action时都会发生这个过程,留下应用程序状态随时间变化的完整的,可序列话的表示形式。

Action接口4
export interface Action {
    type: string;
    payload?: any;
}
派发Action的流水线5

ngrx/store综合介绍,读完马上精通angular中的ngrx/store

Actions简单示例
//没带数据的action
dispatch({type: 'DECREMENT'});

//带数据的action
dispatch({type:ADD_TODO, payload: {id: 1, message: 'Learn ngrx/store', completed: true}});
数据投影

最后,我们需要从Store中提取、组合和投影数据以显示在我们的视图中。因为Store本身是可观察的,所以我们可以访问你习惯的典型JS集合操作(map, filter, reduce等)以及强大的基于RxJS的可观察操作符。这将使得将Store数据分割成你希望很容易的投影。

状态投影
//最简单的示例,从state获取people
store.select('people');

//合并多个state
Observable.combineLatest(
    store.select('people'),
      store.select('events'),
      (people, events) => {
        // 在此投影
    }
)

Not Your Classic Angular

在上一节中,我提到了在开发应用程序时遵守的约定。在传统的设置和工作流程,你已经习惯了吗,这是什么意思?让我们一起来看看。

如果你是从Angular1转过来的,你会很熟悉数据的双向绑定6。控制器把model绑定到视图,反之亦然。这种方法的问题出现在你的视图变得更新复杂时,需要控制器和指令来管理并表示重要的状态随时间的变化。这很快变成一个噩梦,无论是推理还是调度,因为一个变化会影响另一个变化,另一个又影响另一个……等等。

Store提升了单向数据流7和显式调度操作的概念。的感受状态更新都缓存在组件中,委托给reducer。在应用程序中启动状态更新的唯一办法是通过调度操作,对应于特定的reducer案例。这不仅使你应用程序的状态改变变得简单,因为更新是集合在一起的,它会在出现错误时留下清晰的线索。

双向数据绑定6
6 Two-Way Data Binding

ngrx/store综合介绍,读完马上精通angular中的ngrx/store

单向数据绑定

ngrx/store综合介绍,读完马上精通angular中的ngrx/store

不使用Store的Counter示例

(在线演示)

@Component({
    selector:'counter',
    template: `
        <div class='counter'>
            <button (click)='increment()'>+</button>
            <button (click)='decrement()'>-</button>
            <h3>{{counter}}</h3>
        </div>
    `
})
export class Counter {
    counter = 0;
      
      increment() {
        this.counter += 1;
    }
  
      decrement() {
        this.counter -= 1;
    }
}
使用Store的Counter示例

(演示)

@Component({
    selector: 'counter',
      template: `
        <div class='content'>
            <button (click)="increment()">+</button>
            <button (click)="decrement()">-</button>
        </div>
    `,
      changeDetection: ChangeDetectionStrategy.OnPush
})
export class Counter {
    counter$: Observable<number>;
  
      constructor(
          private store: Store<number>
      ){
         this.counter$ = this.store.select('counter');       
     }
  
      increment(){
        this.store.dispatch({type:'INCREMENT'});
    }
  
      decrement(){
        this.store.dispatch({type:'DECREMENT'});
    }
}
Store的优势

在整个概述中,我们简要介绍了利用Store在一种典型的Angular 1风格方法的优势,现在让我们发一点时间来回顾一下。为什么要花时间在这个特定的库,构建和曲线上投资呢?Store的优势是状态中心化,性能,测试。

中心化,状态不可变

所有相关应用程序的状态都缓存在一个位置。这样可以很容易地跟踪问题,因为错误时的状态快照可以提供重要的见解,并且可以轻松的重新重现这个问题。这也使得众多困难问题,例如在Store应用程序的上下文中撤消/重做某一步骤,并且实现了更强大的功能的工具。

性能

由于状态集合中应用程序的顶层,因为数据更新可以通过组件依赖于Store。Angular构建如这样的数据流布置进行优化,并且可以在组件依赖于没有发布新值的Observables的情况下禁用变化检测。在最佳的缓存解决方案中,这将是绝大多数组件。

测试

所有状态更新都是在recudes中处理的,它们是纯函数。纯函数测试非常简单,因为它只是输入,反对输出。这样可以测试应用程序中最关键的方面,而无需使用mock,或其他的测试技巧,可以使测试复杂且容易出错。

工具与生态系统

中心化的,不可变的状态还可以实现更强大的工具。一个这样的盒子是ngrx开发工具,它提供了action和状态变化的历史,允许在开发过程中进行8次遍历。Store提供的模式还允许一个易于实现中间件的丰富的生态系统。因为Store在分派action之前和之后都提供一个入口点,所以应用程序减少,如同步片状态到本地Store,高级日志记录和实现sagas这样的问题可以通过快速包和几行代理来解决。这个生态系统只会在未来几个月内增长。

操作调度action和状态更改的历史 ,以模拟应用程序交互的时间点。

@ngrx/store的构建模块

在构建Store应用程序之前,首先来看看构建@ngrx/store的RxJS概念。首先理解这些概念,我们将来可以更有效地利用这个库。要详细说明下面的每个主题,请查看这些额外的资源。

声明:Mike Ryan和Rob Wormald的实际@ngrx/store代码显着更强大。这些示例旨在演示涉及的RxJS概念,并从库中移出”magic”

Subject/Dispatch的探索

Rx的信使们,你告诉我,我会告诉他们的…..

(演示)

@ngrx/store的两个支柱,Store和Dispatcher都扩展了RxJS主题。主题即是观察者(Observables)和观察者(Observers),这意味着你可以订阅Subject,但也可以将主题订阅源。在高级别科目可以被认为是信使或代理人。

因为Subject是Observables,你可以 “next” 或直接将值传递到流中。然后,该Subject的订阅将被通知发出值。在Store的上下文中,这些用户可能是一个Angular 服务, 组件或需要访问应用程序状态的任何内容。

订阅主题
//创建一个主题
const mySubject = new Rx.Subject();

//添加订阅者
const subscriberOne = mySubject.subscribe(val => {
    console.log('***SUBSCRIBER ONE***',val);
});

const subscriberTwo = mySUbject.subscribe(val => {
    console.log('***SUBSCRIBER TWO***',val);
});

//发射subject的值到observers
mySubject.next('FIRST VALUE!');// ***SUBSCRIBER ONE*** FIRST VALUE! ** SUBSCRIBER TWO*** FIRST VALUE!
mySubject.next('SECOND VALUE!');//***SUBSCRIBER ONE*** SECOND VALUE! ***SUBSCRIBER TWO*** SECOND VALUE

在Store或Redux中,将action发送到应用程序中的Store是一种惯例。为了维护此API,Dispatcher扩展至Subject,将派生方法作为传递添加到传统的下一个方法。这被用于将值发送到Subject中,然后将这些值发送给子对象。

将Dispatcher继承自Subject
/*
redux/ngrx-store 有一个dispatcher的概念,或者是面向应用程序Store发送操作的方法允许扩展Rx.Subject与我们的Dispatcher类来维护熟悉的术语。
*/

//从Subject中继承
class Dispatcher extends Rx.Subject {
    dispatcher(value: any): void{
        this.next(value);
    }
}

//创建一个dispatcher(只是一个包含next的SUbject方法)
const dispatcher = new Dispatcher();

//添加订阅
const subscribeOne = dispatcher.subscribe(val => {
    console.log('***SUBSCRIBER ONE***', val);
});

const subscribeTwo = dispatcher.subscribe(val => {
    console.log('***SUBSCRIBER TWO***', val);
});

//将值发射到observers
dispatcher.dispatch('FIRST DISPATCHED VALUE!');
dispatcher.dispatch('SECOND DISPATCHED VALUE!');
BehaviorSubject/Store探索

与Subject类似,但你说的最后一件事是什么?…

(演示)

虽然Subject作为dispatcher完美地工作,但它们有一个问题可以防止他们适合Store。订阅Subject时,只接收订阅后发出的值。在不断添加和删除组件的环境中,这是不可接受的,在订阅时需要应用程序Store的最新的按需状态部分。

Subjects只接受订阅后发出的值

/*
现在我们有一个dispatcher, 让我们创建我们的Store来接收已经发送的action。
*/

class FirstStore extends Rx.Subject{}

const myFirstStore = new FirstStore();

//添加订阅者
const subscriberOne = myFirstStore.subscribe(val => {
    console.log('***SUBSCRIBER ONE***', val);
});
const subscriberTwo = myFirstStore.subscribe(val => {
    console.log('***SUBSCRIBER TWO***', val);
});

//现在,让超级dispatcher发布值到store
myFirstStore.next('FIRST VALUE!');

/*
我们在添加一个订阅者。
由于我们第一次实施Store是一个subject,订阅者只能看到发布的价值*AFTER*他们订阅之后。在这种情况下,订阅者3将不了解'FIRST VALUE!'
*/
const subscriberThree = myFirstStore.suscribe(val => {
    console.log('***SUBSCRIBER THREE***', val);
});

幸运的是,RxJS为Subject处理这个问题提供了BehaviorSubject。 即BehviorSubject 封装了Subject的所有功能,但也可以在订阅后将改后发布的值返回给订阅都。这意味着组件和服务将始终可以访问最新(或初始值)应用程序状态和所有将来的更新。

BehaviorSubject订阅接收上一次发布的值
/*
因为我们的组件需要查询当前状态,所以BehaviorSubject更适合Store。BehaviorSubjects具有Subject的所有功能,还允许设置初始值,以及在订阅时将所接收的最后一个值输出给所有观察者。
*/
class Store extends Rx.BehaviorSubject {
    constructor(initialState: any){
        super(initialState);
    }
}

const store = new Store('INITIAL VALUE');

//添加一些订阅者
const storeSubscriberOne = store.subscribe(val => {
    console.log('***STORE SUBSCRIBER ONE***', val);
});

//为了演示,手动发布值到store
const storeSubscriberTwo = store.subscribe(val => {
    console.log('***STORE SUBSCRIBER TWO***', val);
});

//在'FIRST VALUE!' 发布之后添加另一个订阅者
//输出:***STORE SUBSCRIBER THREE*** FIRST STORE VALUE!
const subscriberThree = store.subscribe(val => {
    console.log('***STORE SUBSCRIBER THREE***', val);
});
Store + Dispatcher数据流

单状态树和单向数据流在Angular …

(演示)

为了store的上下文正常运行,dispatcher仍然需要一些工作。在Store应用程序中,所有dispatch的action必须通过特定的管道传递,才能将新的状态表示传递到store中,并发送给所有观察者。你可以将此视为工厂装配线,在这种情况下,线上的站是pre-middleare->reducers->post->middleware->store。

这个流水线的创建是在创建时dispatch传递给store处理的。然后,store下一个方法被覆盖,以便将新的状态表示传递到store之前,首先将所有的action都dispatch管道。这也允许通过dispatch汇集接收到的action。

现在,中间件和reducers的实现将被删除。

将Dispatcher与Store关联一起
/*
所有action都应通过管道,然后新计算的状态通过store。
1.) Dispatched Action
2.) Pre-Middleware
3.) Reducers (return new state)
4.) Post-Middleware
5.) store.next(newState)
*/
class Dispatcher extends Rx.Subject{
    dispatcher(value: any): void{
        this.next(value);
    }
}

class Store extends Rx.BehaviorSubject{
    constructor(
          private dispatcher,
         initialState
      ){
            super(initialState);
          /*
          所有dispatch的action在通过新状态之前 通过action管道传递到store
          */
          this.dispatcher
              //pre-middleware
              //reducers
              //post-middleware
              .subscribe(state => super.next(state));
        }
    
      //首先通过分派action到管道并委托给store.dispatch
      dispatch(value){
        this.dispatcher.dispatch(value);
    }
    
      //覆盖store允许直接订阅action注通过store
      next(value){
        this.dispatcher.dispatch(value);
    }
}

const dispatcher = new Dispatcher();
const store = new Store(dispatcher,'INITIAL STATE');

const subscriber = store.subscribe(val => console.log('VALUE FROM STORE: ${val}'));

/*
所有分派action首先流经管道,计算新状态然后传递到store。总结一下,我们的理想行为分派action->premiddleware->reducers->post-middleware->store.next(newState)
*/
//两种方法在幕后都是相同的
dispatcher.dispatch('DISPATCHED VALUE!');
store.dispatch('ANOTHER DISPATCHED VALUE!');

const actionStream$ = new Rx.Subject();

/*
覆盖store下一个方法允许我们将store直接订阅到action流,提供与手动调用store.dispatch或dispatcher.dispatch相同的行为
*/
actionStream$.subscribe(store);
actionStream$.next('NEW ACTION!');
什么是Reducer?

像雪球一样下滑,reducer通过迭代累加…

(演示)

Reducers是基于任何store或Redux的应用基础,描述基于分派action类型的状态部分及其潜在转换。你的reducer的组合是在任何给定时间组成应用程序状态的表示。

在讨论如何创建和实现reducers之前 , 我们先来看看reduce函数。reduce需要一个数组,根据累加值和当前值运行一个函数,在完成后将数组递减一个值。你可以把reducers看成一个滚雪而下的雪橇,每一次变革都会变得很大。以相同的方式,减少reduce是通过迭代定义的函数应用于当前值的结果。

标准的Reduce
/*
你可以想一下滚雪球的场景。每一次翻滚都会累加质量和体积直到到达底部。reduce也类似,返回的值传递给所有提供函数的下一个调用,直到源数组中的所有值都耗尽为止。让我们看看一些巩固概念的盒子。
*/
const numberArray = [1,2,3];

/*
1.) accumulator:1, current:2
2.) accumulator:3, current:3
Final: 6
*/
const total = numberArray.reduce((accumulator, current) => accumulator + current);
console.log('***TOTAL***:',${total});

//reduce操作的对象
const personInfo = [{name:'Joe'},{age:31},{birthday:'1/1/1985'}];

/*
1.) accumulator: {name: 'Joe'}, current: {age: 31}
2.) accumulator: {name: 'Joe', age:31}, current: {birthday: '1/1/1985'}
Final: {name: 'Joe', age:31, birthday: '1/1/1985'}
*/
const fullPerson = personInfo.reduce(accumulator, current) => {
    return Object.assign({}, accumulator, current);
}
console.log('*** FULL PERSON***:',fullPerson);

const personInfoStart = [{name:'Joe'},{age: 31},{birthday:'1/1/1985'}];

/*
1.) accumulator: {favoriteLangue: 'JavaScript'}, current: {name: 'Joe'}
2.) accumulator: {favoriteLangue: 'JavaScript', name: 'Joe'}, current: {age: 31}
3.) accumulator: {favoriteLange: 'JavaScript', name: 'Joe', age: 31}, current: {birthday: '1/1/1985'}
Final: {favoriteLangue: 'JavaScript', name: 'Joe', age: 31, birthday: '1/1/1985'}
*/
const fullPersonStart = personInfo.reduce((accumulator, current) => {
    return Object.assign({}, accumulator, current);
},{favoriteLangue:'JavaScript'});
console.log('***FULL PERSON START:', fullPersonStart);

受Redux的启发,@ngrx/store具有操纵特定状态的Reducer功能的概念。Reducer接受一个state和action作为参数,暴露一个switch语句(一般来说,尽管 这可以通过多种方式处理)定义reducer所涉及的action类型。每次分派一个action时,将调用注册到store的每个reducer(通过根reducer, 在应用程序引导时在provideStore中创建),传递该状态片段(累加器)的当前状态和已分派的action。如果reducer没有被注册来处理该action类型,则将执行适当的状态计算和状态输出的表示。如果 没有那么该部分的当前 状态将被返回。这是Store和Redux的状态管理核心。

Store / Redux 风格的Reducer
// Redux风格的Reducer
const person = (state = {}, action ) => {
    switch(action.type){
      case 'ADD_INFO':
            return Object.assign({}, state, action.payload);
      default:
            return state;
    }
}

const infoAction = {type: 'ADD_INFO', payload: {name:'Brian', framework:'Angular'}};
const anotherPersonInfo = person(undefined, infoAction);
console.log('***REDUX STYLE PERSON***:', anotherPersonInfo);

//添加其他reducer
const hoursWorked = (state = 0, action) => {
    switch(action.type) {
      case 'ADD_HOUR':
            return state + 1;
      case 'SUBTRACT_HOUR':
            return state -1;
      default:
            return state;
    }
}

//组合Reducers更新数据 
const myReducers = { person, hoursWorked};
const combineReducers = reducers => (state = {}, action) => {
    return Object.keys(reducers).reduce((nextState, key) => {
        nextState[key] = reducers[key](state[key],action);
      return nextState;
    }, {});
};

/*
这让我们大部的方式在那里,但真正希望我们想要的是第一个和第二个的值累加随着action随着时间推移。幸运的是,RxJS为这处情况提供了完美的操作符,将在下一课中讨论。
*/
const rootReducer = combineReducers(myReducers);
const firstState = rootReducer(undefined, {type:'ADD_INFO', payload:{name: 'Brian'}});
const secondState = rootReducer({hoursWorked: 10, person: {name: 'Joe'}},{type:'ADD_HOUR'});
console.log('***FIRST STATE***:',firstState);
console.log('***SECOND STATE***:',secondState);
通过根Reducer分派action9

ngrx/store综合介绍,读完马上精通angular中的ngrx/store

使用scan操作符聚合状态

类似于reduce,但值随着时间的推移累加。。。

(scan demo | demo)

scan操作符以类似的方式扮作reduce,除了累加器随时间保持,或直接scan应用的可观察完成。例如,当分派action和新的状态输出时,scan函数 中的累加器将始终是状态的最后一个输出表示形式。这减轻了需要维护store中的状态副本以传递给我们的reducer。

scan操作符的基本示例
const testSubject = new Rx.Subject();
//scan示例,从0开始每次累加
const basicScan = testSubject.scan((acc, curr) => acc+ curr, 0);
// 记录累加值
const subscribe = basicScan.subscribe(val => console.log('Accumulated total:', val));
//传递值到我们的testSubject,并累加当前值
testSubject.next(1);//1
testSubject.next(2);//2
testSubject.next(3);//3

const testSubjectTwo = new Rx.Subject();
// scan示例随着时间的推移建立对象
const objectScan = testSubjectTwo.scan((acc, curr) => Object.assign({}, acc,curr), {});
// 记录累加值
const subscribe = objectScan.subscribe(val => console.log('Accumulated object:', val));
//传递值到testSubject,添加属性到一个新对象
testSubjectTwo.next({name: 'Joe'});
testSubjectTwo.next({age: 30});
testSubjectTwo.next({favoriteFramework: 'Angular 2'});// {name: 'Joe', age: 30, favoriteFramework: 'Angular 2'}

为了在应用程序store中使用scan,它只需要操作符应用于dispatcher程序。所有分派的action都将通过scan,调用具有当前state和action组合的reducer,输出新的状态表示。然后,就的应用程序状态被关闭,或被推送到store,并发送给所有订阅者。

使用scan做store存储
class Store extends Rx.BehaviorSubject{
    constructor(
          private dispatcher,
         private reducer,
         initialState = {}
      ){
            super(initialState);
          this.dispatcher
                // pre-middleware?
              /*
              
              */
          .scan((state, action) => this.reducer(state, action), initialState)
          //post-middleware?
          .subscribe(state => super.next(state));
        }
          // ... store implementation
}
使用let管理中间件

让我拥有整个可观察的。。。

(let demo | store demo)

中间件已经在ngrx/store v2中移出了。通这个部分来阅读本书,以了解let操作符,因为它可以与选择器一起使用。

虽然大多数运算符都是从可观察的值传递出来的,但是我们可以把整个可观察的数据传递出去在返回源可观察数据之前,这允许有机会处理额外的操作符和功能。虽然这可能看起来像一个小小的细微差别,但它完全适合于中间件或选择器(稍后讨论)的情况,消费者想要定义一个可利用的,可重复使用的代码块,以插入到可观察链中的特定时隙。

let 的基本功能
const myArray = [1,2,3,4,5];
const myObservableArray = Rx.Observable.fromArray(myArray);

const test = myObservableArray
    .map(val => val +1)
    //
    //
    .subscribe(val => console.log('VALUE FROM ARRAY:', val));

const letTest = myObservableArray
    .map(val => val +1)
    //
    .let(obs => obs.map(val => val +2))
    .subscribe(val => console.log('VALUE FROM ARRAY WITH let :', val));

const letTestThree = myObservableArray
    .map(val => val +1)
    //
    .let(obs => obs
            .map(val => val +2)
            .filter(val => val % 2 === 0)
     )
    .subscribe(val => consle.log('let WITH MULTIPLE OPERATORS:', val));

const obsArrayPlusYourOperators = (yourAppliedOperators) => {
    return myObservableArray
          .map(val => val +1 )
          .let(yourAppliedOperators)
};

const addTenThenTwenty = obs => obs.map(val => val + 10).map(val => val + 20);
const letTestFour = obsArrayPlusYourOperators(addTenThenTwenty)
    .subscribe(val => console.log('let FROM FUNCTION:', val));
添加let 操作符到中间件的入口
class Store extends Rx.BehaviorSubject{
    constructor(
      private dispatcher,
     private reducer,
     preMiddleware,
     postMiddleware,
     initialState = {}
  ){
        super(initialState);
      this.dispatcher
          //
          //
          //
          //
      .let(preMiddleware)
      .scan((state, action) => this.reducer(state,action),initialState)
      .let(postMiddleware)
      .subscribe(state => super.next(state));
    }
  // ... store implementation
}

const preMiddleware = obs => { return obs.do(val => console.log('ACTION:', val))};
const postMiddleware = obs => {return obs.do(val => console.log('STATE:', val))};

... create store supplying middleware
class Dispatcher extends Rx.Subject{
    dispatch(value: any) : void{
        this.next(value);
    }
}

class Store extends Rx.BehaviorSubject{
    constructor(
          private dispatcher,
         private reducer,
         preMiddleware,
         postMiddleware,
         initialState = {}
      ){
            super(initialState);
          this.dispatcher
              .let(preMiddleware)
              .scan((state,action) => this.reducer(state, action), initialState)
              .let(postMiddleware)
              .subscribe(state => super.next(state));
        }
  
  
        //
        //
        select(key:string){
            return this.map(state => state[key]);
        }

        // ... store implemenetation
}
//... create store

//
const subscriber = store
    .select('person')
    .subscribe(val => console.log('VALUE OF PERSON:', val));
//
const myArrayWithDuplicateInARow = new Rx.Observable    
    .fromArray([1,1,2,2,3,1,2,3]);

const distinctSub = myArrayWithDuplicatesInARow
    .distinctUntilChanged()
    //output: 1,2,3,1,2,3
    .subscribe(val => console.log('DISTINCT SUB:',val));

const nonDistinctSub = myArrayWithDuplicatesInARow
    //output: 1,1,2,2,3,1,2,3
    .subscribe(val => console.log('MON DISTINCT SUB:', val));

const sampleObject = {name: 'Test'};

const myArrayWithDuplicateObjects = new Rx.Observable.fromArray([sampleObject,sampleObject,sampleObject]);
//
const nonDistinctObjects = myArrayWithDuplicateObjects
    .distinctUntilChanged()
    //output: 'DISTINCT OBJECTS: {name: 'Test'}
    .subscribe(val => console.log('DISTINCT OBJECTS:',val));
distinctUntilChanged With Store
class Dispatcher extends Rx.Subject {
    dispatch(value:any) : void{
        this.next(value);
    }
}

class Store extends Rx.BehaviorSubject{
    constructor(
          private dispatcher,
         private reducer,
         preMiddleware,
         postMiddleware,
         initialState = {}
      ){
            super(initialState);
          this.dispatcher
              .let(preMiddleware)
              .scan((state, action) => this.reducer(state,action), initialState),
            .let(postMiddleware)
              .subscribe(state => super.next(state));
        }
  
  /*
      
  */
  select(key:string){
      return this.map(state => state[key])
                .distinctUntilChanged();
  }
}

// add reducers
  // configure store
  
  const subscriber = store
    //
    .select('person')
    .subscribe(val => console.log('PERSON WITH DISTINCTUNTILCHANGED:', val));

const subscriberTwo = store
    //
    .map(state => state.person)
    .subscribe(val => console.log('PERSON WITHOUT DISTINCTUNTILCHANGED:', val));

    // dispatch a few actions

    dispatcher.dispatch({
        type:'ADD_INFO',
      payload:{
          name:'Brian',
        message:'Exporing Reduce!'
      }
    });

dispatcher.dispatch({
    type:'ADD_HOUR'
});

以上内容未完,待续

2017.8.19 星期六 深圳

参考资料

https://gist.github.com/corey…


发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>