From ac366fe7ed2e7a011ff0baba239dc9542480169f Mon Sep 17 00:00:00 2001 From: Mikael Brassman Date: Wed, 18 Nov 2015 14:31:44 +0100 Subject: [PATCH] Initial implementation for reducers --- docs/api/Reflux.md | 22 +++++++++++++++++ package.json | 2 +- src/createReducer.js | 27 ++++++++++++++++++++ src/index.js | 2 ++ test/creatingReducers.spec.js | 46 +++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/createReducer.js create mode 100644 test/creatingReducers.spec.js diff --git a/docs/api/Reflux.md b/docs/api/Reflux.md index 430f491..941dd01 100644 --- a/docs/api/Reflux.md +++ b/docs/api/Reflux.md @@ -30,6 +30,28 @@ Creates an event emitting Data Store. It is mixed in with functions from the `Li @returns {Store} A data store instance +## Reflux.createReducer(initialData) + +Creates a reducer, a data store that uses pure functions to mutate the data. + +@param {Mixed} initialData the initial data to seed the reducer + +@returns {Reducer} A reducer instance + +@example +```javascript +var addUser = Reflux.createAction({ sync: true }); +var reducer = Reflux.createReducer({ users: [] }); + +reducer.on(addUser, function(data, newUser) { + data.users.push(newUser); + return data; +}) + +addUser("John Doe"); +reducer.peek(); // outputs { users: ["John Doe"] } +``` + ## Reflux.joinTrailing(...publishers) Creates a static join for the given publishers. It emits when all the publishers have emitted at least once, and will emit with the data that was last emitted by each listenable. diff --git a/package.json b/package.json index b5b8fa5..90f0b4f 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "benchmark": "node test/benchmarks", "watch:lint": "esw -w", "watch:test": "mocha -w -R min --compilers js:babel-core/register ./test", - "watch": "parallelshell 'npm run watch:test' 'npm run watch:lint'", + "watch": "parallelshell 'npm run watch:lint' 'npm run watch:test'", "prepublish": "npm run lint && npm run test:mocha && npm run clean && npm run compile", "precommit": "npm run lint && npm run test:mocha", "prepush": "npm run precommit" diff --git a/src/createReducer.js b/src/createReducer.js new file mode 100644 index 0000000..aba9a1d --- /dev/null +++ b/src/createReducer.js @@ -0,0 +1,27 @@ +import * as _ from "./utils"; +import PublisherMethods from "./PublisherMethods"; + +export default function createReducer(initialData) { + + var data = initialData; + + function Reducer() { + this.emitter = new _.EventEmitter(); + this.eventLabel = "reducer"; + } + + Reducer.prototype.on = function(publisher, reducerFn) { + publisher.listen(function() { + data = reducerFn.apply(this, [ data, ...arguments ]); + this.trigger(data); + }, this); + }; + + Reducer.prototype.peek = function() { + return data; + }; + + _.extend(Reducer.prototype, PublisherMethods); + + return new Reducer(); +} diff --git a/src/index.js b/src/index.js index 4d368c6..69f1397 100644 --- a/src/index.js +++ b/src/index.js @@ -18,6 +18,7 @@ import * as _ from "./utils"; const utils = _; import createAction from "./createAction"; import createStore from "./createStore"; +import createReducer from "./createReducer"; /** * Convenience function for creating a set of actions @@ -83,6 +84,7 @@ export default { utils, createAction, createStore, + createReducer, createActions, setEventEmitter, nextTick, diff --git a/test/creatingReducers.spec.js b/test/creatingReducers.spec.js new file mode 100644 index 0000000..6ba1dc0 --- /dev/null +++ b/test/creatingReducers.spec.js @@ -0,0 +1,46 @@ +import chai, { assert } from 'chai'; +import asPromised from 'chai-as-promised'; +import Reflux from "../src"; +import sinon from "sinon"; + +chai.use(asPromised); + +describe('with a reducer', function() { + describe('that has initial data', function() { + var reducer, initialData; + + beforeEach(() => { + initialData = { + datum: 1337 + }; + reducer = Reflux.createReducer(initialData); + }); + + it('should be able to peek the data', function() { + assert.equal(initialData.datum, reducer.peek().datum); + }); + + describe('hooked to an action', function() { + var action, listener; + + beforeEach(() => { + action = Reflux.createAction({sync: true}); + listener = sinon.spy(); + reducer.listen(listener); + }); + + it('should pass previous data as first argument', function() { + reducer.on(action, (a) => a); + action(); + return assert.deepEqual(listener.args[0][0], initialData); + }); + + it('should pass arguments to the rest', function() { + reducer.on(action, (a, b, c) => { return {b: b, c: c}; }); + action("dude", 1337); + return assert.deepEqual(reducer.peek(), {b: "dude", c: 1337}); + }); + + }); + }); +});