项目作者: atellmer

项目描述 :
Reactive management for javaScript applications
高级语言: JavaScript
项目地址: git://github.com/atellmer/spawn-x.git
创建时间: 2016-11-01T20:02:43Z
项目社区:https://github.com/atellmer/spawn-x

开源协议:MIT License

下载


spawn-x

Reactive management for javaScript applications

Spawn

About

Spawn is a simple and super small library (8 kb) without dependencies for reactive management of app state which use modified observer pattern, where instead names of events, uses zones - paths to data into app state.

You can use Spawn independently with another libraryes.
Also you may be interested:

install

With npm:

  1. npm install spawn-x --save

With yarn:

  1. yarn add spawn-x
  1. import { createStore } from 'spawn-x';
  2. const store = createStore();

With bower:

  1. bower install spawn-x --save
  1. <script src="path/to/spawn-x/lib/spawn-x.umd.min.js"></script>
  1. var store = Spawn.createStore();

API:

Spawn exports 2 simple functions: createStore() and addInterceptor().

createStore()

This function needed for first initialize store.

  1. // Signature:
  2. createStore(initialState?: any, addInterceptor?: func): instance
  1. // Examples
  2. const store = createStore();
  3. // with initial state
  4. const initialState = {
  5. hello: 'world'
  6. };
  7. const store = createStore(initialState);

addInterceptor()

This function needed if you want to add interceptors (middlewares). For example interceptor may be as logger.

  1. addInterceptor(interceptor: func, ...): Array<interceptors>
  1. // Examples
  2. const myLoggerInterceptor = store => next => action => {
  3. console.log('action: ', action);
  4. next(action);
  5. }
  6. const myOtherInterceptor = store => next => action => next(action);
  7. const store = createStore({}, addInterceptor(myLoggerInterceptor, myOtherInterceptor));
  8. //or without initialState
  9. const store = createStore(addInterceptor(myLoggerInterceptor, myOtherInterceptor));

Store object after initialization will only have 4 methods: select(), detect(), reject(), update()

select()

Method return selected zone from app state. If zone will be equal ‘*’, this method returns full app state. if zone will be a function, method puts the app state in the function argument and apply it.

  1. // Signature:
  2. select(zone: string | func): any
  1. // Examples:
  2. store.select('foo.bar');
  3. store.select('*'); // full app state
  4. store.select(state => state.foo.bar[2]); // ES2015
  5. store.select(function (state) { return state.foo.bar[2] }); // ES5

detect()

Method makes subscribe for data zone change and apply callback if zone updated. If zone will be equal ‘*’, this method makes subscribe for all changes. Returns instance object.

  1. // Signature:
  2. detect(zone: string, callback: func): instance
  1. // Examples:
  2. const callback = () => {
  3. const data = store.select('foo.bar');
  4. }
  5. store.detect('foo.bar', callback);
  6. store.detect('*', () => {
  7. console.log('something happened!');
  8. });
  9. //with receipt of action
  10. store.detect('*', action => {
  11. if (action.type === 'KARAMBA') {
  12. store.update('foo', { data: 'bar', type: 'KARAMBA_DETECTED' })
  13. }
  14. });

reject()

Method for the removal of a callback (unsubscribe).

  1. // Signature:
  2. reject(zone: string, callback: func): instance
  1. // Examples:
  2. const callback = () => {
  3. const admins = store.select('foo.bar');
  4. }
  5. store.reject('foo.bar', callback);

update()

Method for updates zone. This method takes zone as first argument and action as second. Action must have ‘data’ field for your data and type. If zone will be equal ‘*’, this method replaces app state on new state and apply all callbacks. It is may be useful to implementation something like time traveling. Returns instance object.

  1. // Signature:
  2. interface IAction {
  3. data: any;
  4. type: string;
  5. }
  6. update(zone: string, action: IAction): instance
  1. // Examples:
  2. const admins = [
  3. { id: 0, name: 'John Doe' },
  4. { id: 1, name: 'Alex Smith' },
  5. { id: 2, name: 'Kate Jensen' },
  6. ];
  7. const myAction = {
  8. data: admins,
  9. type: 'UPDATE_ADMINS'
  10. }
  11. store.update('roles.admins', myAction);
  12. //load app state from localStorage
  13. const myAction = {
  14. data: JSON.parse(localStorage.getItem('APP_STATE')),
  15. type: 'LOAD_STATE'
  16. }
  17. store.update('*', myAction);

Note:

You can subscribe on not fully matching zones, and Spawn will runs callbacks correctly. For example: if you subscribe on ‘grandpa.parent.child’ and will update ‘grandpa’ or ‘grandpa.parent’, then ‘grandpa.parent.child’ will launch own callback. in its turn, if you subscribe on ‘grandpa’ and will update ‘grandpa.parent’ or ‘grandpa.parent.child’, then ‘grandpa’ will launch own callback.

Example #1

  1. import { createStore } from 'spawn-x';
  2. const store = createStore();
  3. function callback() {
  4. console.log('name: ', store.select(state => state.users.admins[0].name));
  5. }
  6. //subscribe only on users.admins
  7. store.detect('users.admins', callback);
  8. //update users
  9. store.update('users', {
  10. data: {
  11. admins: [
  12. { id: 0, name: 'John' },
  13. { id: 1, name: 'Alex' }
  14. ]
  15. },
  16. type: 'UPDATE_USERS'
  17. });
  18. //console output: 'name: John'
  19. setTimeout(() => {
  20. store.update('users', {
  21. data: {
  22. admins: [
  23. { id: 0, name: 'Jess' },
  24. { id: 1, name: 'Alex' }
  25. ],
  26. some: 'text'
  27. },
  28. type: 'UPDATE_USERS'
  29. });
  30. }, 2000);
  31. //console output: 'name: Jess'

Example #2 “Simple todo app”

  1. import { createStore, addInterceptor } from 'spawn-x';
  2. class TodoApp {
  3. constructor(store) {
  4. this.store = store;
  5. this.store.detect('today.tasks', () => combineActions(this.store.select('today.tasks')));
  6. }
  7. addTask(task) {
  8. const tasks = this.store
  9. .select('today.tasks')
  10. .concat(task);
  11. this.store.update('today.tasks', {
  12. data: tasks,
  13. type: 'ADD_TASK'
  14. });
  15. }
  16. removeTask(id) {
  17. const filteredTasks = this.store
  18. .select('today.tasks')
  19. .filter(task => task.id !== id);
  20. this.store.update('today.tasks', {
  21. data: filteredTasks,
  22. type: 'REMOVE_TASK'
  23. });
  24. }
  25. completeTask(id, complete) {
  26. const updatedTasks = this.store
  27. .select('today.tasks')
  28. .map(task => {
  29. if (task.id === id) {
  30. task.complete = complete;
  31. }
  32. return task;
  33. });
  34. this.store.update('today.tasks', {
  35. data: updatedTasks,
  36. type: 'CHANGE_COMPLETE'
  37. });
  38. }
  39. }
  40. function combineActions(todos) {
  41. console.log('All todos: ', reportAction(todos));
  42. console.log('Completed todos:', getCountCompletedAction(todos));
  43. console.log('-----');
  44. }
  45. function reportAction (todos) {
  46. return todos.length;
  47. }
  48. function getCountCompletedAction(todos) {
  49. return todos.filter(todo => todo.complete === true).length;
  50. }
  51. function logger(store) {
  52. return next => action => {
  53. console.log('action: ', action.type + ' -> ', JSON.parse(JSON.stringify(action.data)));
  54. next(action);
  55. }
  56. }
  57. ///////////////////////////
  58. const initialState = {
  59. today: {
  60. tasks: []
  61. }
  62. };
  63. const store = createStore(
  64. initialState,
  65. addInterceptor(logger)
  66. );
  67. const app = new TodoApp(store);
  68. app.addTask({
  69. id: 0,
  70. action: 'Learn React',
  71. complete: true
  72. });
  73. app.addTask({
  74. id: 1,
  75. action: 'Learn Angular',
  76. complete: true
  77. });
  78. app.addTask({
  79. id: 2,
  80. action: 'Don\'t be the asshole',
  81. complete: false
  82. });
  83. app.completeTask(2, true);
  84. app.removeTask(1);
  85. /*
  86. console output:
  87. action: @@SPAWN/INIT -> ...
  88. All todos: 0
  89. Completed todos: 0
  90. -----
  91. All todos: 1
  92. Completed todos: 1
  93. action: ADD_TASK -> ...
  94. -----
  95. All todos: 2
  96. Completed todos: 2
  97. action: ADD_TASK -> ...
  98. -----
  99. All todos: 3
  100. Completed todos: 2
  101. action: ADD_TASK -> ...
  102. -----
  103. All todos: 3
  104. Completed todos: 3
  105. action: CHANGE_COMPLETE -> ...
  106. -----
  107. All todos: 2
  108. Completed todos: 2
  109. action: REMOVE_TASK -> ...
  110. */

Example #3 “Redux-like style”

  1. import { createStore } from 'spawn-x';
  2. const btn = document.querySelector('#addTrack');
  3. const input = document.querySelector('#input');
  4. const list = document.querySelector('#trackList');
  5. const store = createStore();
  6. // Constants
  7. const ADD_TRACK = 'ADD_TRACK';
  8. const RENDER_TRACKS = 'RENDER_TRACKS';
  9. const UPDATE_STORE = 'UPDATE_STORE';
  10. // fake Reducer
  11. store.detect('*', action => {
  12. console.log(action);
  13. switch(action.type) {
  14. case ADD_TRACK: {
  15. store.update('tracks', {
  16. type: UPDATE_STORE,
  17. data: store.select('tracks') ? store.select('tracks').concat(action.data) : [].concat(action.data)
  18. });
  19. }
  20. }
  21. });
  22. // Action Creators
  23. const addTrack = data => {
  24. store.update('', {
  25. type: ADD_TRACK,
  26. data: data
  27. });
  28. }
  29. const renderTracks = () => {
  30. list.innerHTML = '';
  31. store
  32. .select('tracks')
  33. .forEach(item => {
  34. const li = document.createElement('li');
  35. li.textContent = item;
  36. list.appendChild(li);
  37. });
  38. store.update('', {
  39. type: RENDER_TRACKS,
  40. data: null
  41. });
  42. }
  43. btn.addEventListener('click', () => {
  44. addTrack(input.value);
  45. renderTracks();
  46. input.value = '';
  47. });

LICENSE

MIT © Alex Plex