diff --git a/Readme.md b/Readme.md index cace503..5ec06ee 100644 --- a/Readme.md +++ b/Readme.md @@ -12,6 +12,7 @@ Subscribe and UnSubscribe action for Redux ## Usage For Middleware + ```js import {createStore} from 'redux'; import Subscriber from 'redux-subscriber-middleware'; @@ -28,6 +29,7 @@ let store = createStore( ``` Inside the script + ```js // or you can just import "subscribe" function from the package import { subscribeAction, subscribeOnceAction, unsubscribeAction } from 'redux-subscriber-middleware'; @@ -38,8 +40,17 @@ dispatch(subscribeAction('ACTION_YOU_WANT_TO_SUBSCRIBE', CALLBACK_FUNCTION)); // for one time dispatch(subscribeOnceAction('ACTION_YOU_WANT_TO_SUBSCRIBE', CALLBACK_FUNCTION)); -// for unsubscribe -dispatch(unsubscribeAction('ACTION_YOU_WANT_TO_SUBSCRIBE')); +// unsubscribe all callbacks for action +dispatch(unsubscribeAction('ACTION_YOU_WANT_TO_UNSUBSCRIBE')); + +// unsubscribe specific callback for action +dispatch(unsubscribeAction('ACTION_YOU_WANT_TO_UNSUBSCRIBE', CALLBACK_FUNCTION)); + +// unsubscribe specific callback for all actions +dispatch(unsubscribeAction(null, CALLBACK_FUNCTION)); + +// unsubscribe everything +dispatch(unsubscribeAction()); ``` diff --git a/__tests__/index.js b/__tests__/index.js index 4575da8..18ea327 100644 --- a/__tests__/index.js +++ b/__tests__/index.js @@ -3,7 +3,8 @@ import Subscriber, { subscribeAction, subscribeOnceAction, unsubscribeAction } f let store; let mockCallback; -const addAction = () => ({ type: 'ADD_ACTION' }); +const addAction = () => ({ type: 'ADD_ACTION' }); +const otherAction = () => ({ type: 'OTHER_ACTION' }); function initialize() { const middlewares = [Subscriber()]; @@ -77,12 +78,12 @@ describe('redux-subscriber-middleware', () => { beforeEach(() => { initialize(); }); - it('should call the callback function one time', () => { + it('should call the callback function two times', () => { store.dispatch(addAction()); store.dispatch(subscribeAction('ADD_ACTION', mockCallback)); store.dispatch(addAction()); store.dispatch(addAction()); - expect(mockCallback.mock.calls.length).toEqual(3); + expect(mockCallback.mock.calls.length).toEqual(2); }); }); }); @@ -95,5 +96,58 @@ describe('redux-subscriber-middleware', () => { store.dispatch(addAction()); expect(mockCallback.mock.calls.length).toEqual(1); }); + + it('should unsubscribe the specific callback given', () => { + let otherCallback = jest.fn(); + let onceCallback = jest.fn(); + + store.dispatch(subscribeAction('ADD_ACTION', mockCallback)); + store.dispatch(subscribeAction('ADD_ACTION', otherCallback)); + store.dispatch(addAction()); + + store.dispatch(subscribeOnceAction('ADD_ACTION', onceCallback)); + expect(onceCallback).not.toBeCalled(); + store.dispatch(unsubscribeAction('ADD_ACTION', otherCallback)); + store.dispatch(unsubscribeAction('ADD_ACTION', onceCallback)); + + store.dispatch(addAction()); + + expect(otherCallback.mock.calls.length).toEqual(1); + expect(mockCallback.mock.calls.length).toEqual(2); + }); + + it('should unsubscribe the specific callback given for all actions', () => { + let onceCallback = jest.fn(); + + store.dispatch(subscribeAction('ADD_ACTION', mockCallback)); + store.dispatch(subscribeAction('OTHER_ACTION', mockCallback)); + store.dispatch(addAction()); + store.dispatch(otherAction()); + + store.dispatch(subscribeOnceAction('ADD_ACTION', onceCallback)); + expect(onceCallback).not.toBeCalled(); + store.dispatch(unsubscribeAction(null, mockCallback)); + store.dispatch(unsubscribeAction(null, onceCallback)); + + store.dispatch(addAction()); + store.dispatch(otherAction()); + + expect(mockCallback.mock.calls.length).toEqual(2); + }); + + it('should unsubscribe ALL THE THINGS', () => { + let otherCallback = jest.fn(); + + store.dispatch(subscribeAction('ADD_ACTION', mockCallback)); + store.dispatch(subscribeAction('OTHER_ACTION', mockCallback)); + store.dispatch(subscribeAction('OTHER_ACTION', otherCallback)); + store.dispatch(addAction()); + store.dispatch(otherAction()); + store.dispatch(unsubscribeAction()); + store.dispatch(addAction()); + store.dispatch(otherAction()); + expect(mockCallback.mock.calls.length).toEqual(2); + expect(otherCallback.mock.calls.length).toEqual(1); + }); }); }); diff --git a/index.js b/index.js index d2a1397..dc9a25d 100644 --- a/index.js +++ b/index.js @@ -1,47 +1,62 @@ -'use strict'; + export const SUBSCRIBE = 'REDUX_ACTION_SUBSCRIBE'; export const SUBSCRIBEONCE = 'REDUX_ACTION_SUBSCRIBEONCE'; export const UNSUBSCRIBE = 'REDUX_ACTION_UNSUBSCRIBE'; export default () => { - const actionList = []; const subscribe = {}; const subscribeOnce = {}; - function subscribeHandler(subscribe, event, cb, isMultiple = false) { - const _subscribe = subscribe; - if (actionList.indexOf(event) >= 0) { - cb(); - isMultiple ? _subscribe[event] = [cb] : ''; - } else if (_subscribe[event]) { - _subscribe[event].push(cb); + function subscribeHandler(repo, event, cb) { + if (repo[event]) { + repo[event].push(cb); } else { - _subscribe[event] = [cb]; + repo[event] = [cb]; // eslint-disable-line no-param-reassign } } - return store => next => (action) => { + return store => next => (action) => { // eslint-disable-line no-unused-vars const result = next(action); if (action.type === SUBSCRIBE || action.type === SUBSCRIBEONCE || action.type === UNSUBSCRIBE) { switch (action.type) { - case SUBSCRIBE: - subscribeHandler(subscribe, action.payload.event, action.payload.callback ,true); - break; - case SUBSCRIBEONCE: - subscribeHandler(subscribeOnce, action.payload.event, action.payload.callback); - break; - case UNSUBSCRIBE: - if (subscribe[action.payload.event]) { - delete subscribe[action.payload.event]; - } - if (subscribeOnce[action.payload.event]) { - delete subscribeOnce[action.payload.event]; - } - break; - default: + case SUBSCRIBE: + subscribeHandler(subscribe, action.payload.event, action.payload.callback, true); + break; + case SUBSCRIBEONCE: + subscribeHandler(subscribeOnce, action.payload.event, action.payload.callback); + break; + case UNSUBSCRIBE: // eslint-disable-line no-case-declarations + const { event, callback } = action.payload; + if (event) { + if (callback) { + if (subscribe[event]) { + subscribe[event] = subscribe[event].filter(cb => cb !== callback); + } + + if (subscribeOnce[event]) { + subscribeOnce[event] = subscribeOnce[event].filter(cb => cb !== callback); + } + } else { + delete subscribe[event]; + delete subscribeOnce[event]; + } + } else if (callback) { + // eslint-disable-next-line no-return-assign + Object.keys(subscribe).forEach(k => + subscribe[k] = subscribe[k].filter(cb => cb !== callback), + ); + // eslint-disable-next-line no-return-assign + Object.keys(subscribeOnce).forEach(k => + subscribeOnce[k] = subscribeOnce[k].filter(cb => cb !== callback), + ); + } else { + Object.keys(subscribe).forEach(k => delete subscribe[k]); + Object.keys(subscribeOnce).forEach(k => delete subscribeOnce[k]); + } + break; + default: } } else { - actionList.push(action.type); if (subscribe[action.type]) { subscribe[action.type].forEach(cb => cb(result)); } @@ -58,21 +73,21 @@ export const subscribeAction = (event, callback) => ({ type: SUBSCRIBE, payload: { event, - callback - } + callback, + }, }); export const subscribeOnceAction = (event, callback) => ({ type: SUBSCRIBEONCE, payload: { event, - callback - } + callback, + }, }); -export const unsubscribeAction = event => ({ - type: UNSUBSCRIBE, - payload: { - event - } -}); +export const unsubscribeAction = (event, callback = null) => { + const action = { type: UNSUBSCRIBE, payload: { } }; + if (event) action.payload.event = event; + if (callback) action.payload.callback = callback; + return action; +};