[AngularJS] Write a simple Redux store in AngularJS app

The first things we need to do is create a reducer: 

/**
 * CONSTANT
 * @type {string}
 */
export const GET_CATEGORIES = "GET_CATEGORIES";

/**
 * INIT VALUE
 */
export const initialCategories = [
  {id: 0, name: 'Development'},
  {id: 1, name: 'Design'},
  {id: 2, name: 'Exercise'},
  {id: 3, name: 'Humor'}
];

/**
 * REDUCERS
 * @type {string}
 */
export const categories = (state = initialCategories, {type, payload}) => {
  switch(type) {
    case GET_CATEGORIES:
      return payload || state;
    default:
      return state;
  }
};

It has some default initialize data. What it does is just simply return the state.

Then let's create a gloable store for the app, which has two methods, getState, dispatch.  Two props: reducer, state.

class Store {
  constructor(reducer, initialState) {
    this.reducer = reducer;
    this.state=  initialState;
  }

  getState() {
     return this.state
  }

  dispatch() {
     this.state = this.reducer(this.state, action);
  }

}

Once we got that, we are going to init our store:

import {categories, initialCategories} from './components/categories/category.state';
import Store from './app.store';
const store = new Store(categories, initialCategories);

We passed in categoreis reudcer and the initialCategories state.

To make it available to Angular APP. we need to make it injectable:

let appModule = angular.module('app', [
    CommonModule.name,
    ComponentsModule.name
  ])
    .value('store', store)

Then we can use it in our app:

class CategoriesController {
  constructor(store) {
    'ngInject';

    angular.extend(this, {
      store
    });
  }

  $onInit() {
    this.store.dispatch({type: GET_CATEGORIES});
    this.categories = this.store.getState();
  }
}

Now we are going to simply the code a little bit, we going to make a subscribe method so that we don't need to call getState() method everytime after we dispatch an action.

You can think that the subscribe method is a just callback function which each time we dispatch an action, it will be called. And inside the callback function, we will call this.store.getState() to get the value.

class Store {
  constructor(reducer, initialState) {
    this.reducer = reducer;
    this.state = initialState;
    this.listeners = [];
  }

  getState() {
    return this.state;
  }

  dispatch(action) {
    this.state = this.reducer(this.state, action);
    this.listeners.forEach((l) => l());
  }

  subscribe(listener) {
    this.listeners = [
      ...this.listeners,
      listener
    ];

    // return an unsubscribe function
    return () => {
      this.listeners = this.listeners.filter(l => l !== listener);
    }
  }
}

export default Store;
class CategoriesController {
  constructor($timeout, store) {
    'ngInject';

    angular.extend(this, {
      $timeout,
      store
    });
  }

  $onInit() {
    this.unsubscribe = this.store.subscribe(() => {
       this.categories = this.store.getState();
    });

    this.store.dispatch({type: GET_CATEGORIES});
  }
}

Currently inside the dispatch() metod, we pass in an object with type and payload. It would be better if we can manage those action in a single place. There is where Action creator comes in to play.

/**
 * ACTIONS CREATOR
 */
export const CategoriesActions = () => {
  const getCategoreis = (categories) => {
    return {type: GET_CATEGORIES, payload: categories}
  };

  const getCurrentCategory = (currentCategory) => {
    return {type: GET_CURRENT_CATEGORY, payload: currentCategory}
  };

  return {
    getCategoreis,
    getCurrentCategory
  };
};

To make it avaiable to Angular App, we can create a factory for this:

let appModule = angular.module('app', [
    CommonModule.name,
    ComponentsModule.name
  ])
    .value('store', store)
    .factory('CategoriesActions', CategoriesActions)
  .component('app', AppComponent)

Then we can use it inside the controller:

  constructor($timeout, store, CategoriesActions) {
    'ngInject';

    angular.extend(this, {
      $timeout,
      store,
      CategoriesActions
    });
  }
  $onInit() {
    this.unsubscribe = this.store.subscribe(() => {
       this.categories = this.store.getState();
    });

    this.store.dispatch(this.CategoriesActions.getCategoreis());
  }
  onCategorySelected(currentCategory) {
    this.currentCategory = category(this.currentCategory, this.CategoriesActions.getCurrentCategory(currentCategory));
  }
原文地址:https://www.cnblogs.com/Answer1215/p/6044626.html