chopped-redux
Advanced tools
Comparing version 3.0.0 to 4.0.0
22
index.js
module.exports = function (reducer, state, listeners) { | ||
listeners = listeners || [] | ||
module.exports = function factory (update, state) { | ||
var listeners = [] | ||
if (typeof reducer !== 'function') { | ||
throw new TypeError('The `reducer` param must be a function.') | ||
if (typeof update !== 'function') { | ||
throw new TypeError('The `update` param must be a function.') | ||
} | ||
@@ -19,4 +19,6 @@ | ||
action = action || {} | ||
state = reducer(state, action) | ||
listeners.forEach(function (fn) { fn(action) }) | ||
state = update(state, action) | ||
listeners.slice().forEach(function (fn) { fn(action) }) | ||
return action | ||
} | ||
@@ -37,4 +39,10 @@ | ||
dispatch: dispatch, | ||
subscribe: subscribe | ||
subscribe: subscribe, | ||
get updater () { | ||
return update | ||
}, | ||
set updater (fn) { | ||
update = fn | ||
} | ||
} | ||
} |
{ | ||
"name": "chopped-redux", | ||
"version": "3.0.0", | ||
"description": "A subset of @gaearon Redux", | ||
"version": "4.0.0", | ||
"description": "An implementation of @gaearon Redux", | ||
"keywords": [ | ||
@@ -19,5 +19,3 @@ "flux", | ||
"license": "MIT", | ||
"dependencies": { | ||
"curry": "^1.2.0" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
@@ -24,0 +22,0 @@ "immutable": "^3.7.4", |
# Chopped Redux | ||
![npm version](https://img.shields.io/npm/v/chopped-redux.svg) ![npm downloads](https://img.shields.io/npm/dm/chopped-redux.svg) | ||
![npm version](https://img.shields.io/npm/v/chopped-redux.svg) | ||
This library is a subset of [@gaearon](https://github.com/gaearon) [Redux](https://github.com/gaearon/redux), which claims to be a "Predictable state container for JavaScript apps". | ||
This library is an implementation (a subset?) of [@gaearon](https://github.com/gaearon) [Redux](https://github.com/gaearon/redux), which claims to be a "Predictable state container for JavaScript apps". | ||
Redux is based on [Facebook's Flux](https://facebook.github.io/flux/) but it's a lot more simple a straightforward. Chopped Redux follows the same principles and ideas but cutting off features. If you care, it's 30 sloc (0.75 kB). | ||
Redux is based on [Facebook's Flux](https://facebook.github.io/flux/) but it's a lot more simple a straightforward. Chopped Redux follows the same principles and ideas but cutting off features, namely all utility methods and ES2015/7 magic. Chopped is practically the same as Redux's 1.0 core, just [read the source](https://github.com/acstll/chopped-redux/blob/master/index.js). | ||
@@ -13,4 +13,14 @@ This project follows [SemVer](http://semver.org/). | ||
In the beginning, Redux was a [React](http://facebook.github.io/react/) thing. So I wanted to have a similar library not tight to any rendering/view-layer library, and I was mainly inspired by [this](https://github.com/gaearon/redux/pull/166) and [this](https://github.com/gaearon/redux/issues/113#issuecomment-114049804), ideas which made the Flux unidirectional data-flow very simple. Redux is [free from React](https://github.com/gaearon/redux/issues/230) starting at 1.0. Still Chopped is a simpler alternative to it (though Redux is itself very small and simple). The things you'll miss from Redux here are basically `Middleware`, ES2015/7 magic and restrictions. Hot-reloading and time-travel are possible if you know what you're doing, or **why** you're doing it, but it's **not built-in**. | ||
In the beginning, Redux was a [React](http://facebook.github.io/react/) thing. So I wanted to have a similar library not tight to any rendering/view-layer library, and I was mainly inspired by [this](https://github.com/gaearon/redux/pull/166) and [this](https://github.com/gaearon/redux/issues/113#issuecomment-114049804), ideas which made the Flux unidirectional data-flow very simple. Redux is [free from React](https://github.com/gaearon/redux/issues/230) and free from `class`es starting at 1.0, so there's no reason for you to use Chopped instead of the genuine Redux if you don't find any of the key differences useful to you. | ||
### Key differences from Redux | ||
- There’s no init dispatch on `createStore()`, you need to do that yourself when you know it’s time to initialize your state. | ||
- You can pass anything to `dispatch()`, not only a plain object, it's your responsibility to handle that in the `update function. | ||
- You can call `dispatch()` with no arguments (an empty object will get dispatched), useful for initializing. | ||
- The dispatched `action` gets passed to listeners. | ||
- The `reducer` function is called `update` (this is just aesthetics). | ||
- There's an extra method `replaceState` (use carefully, the whole point of Redux is to make state changes sane and predictable). | ||
- `getReducer` and `replaceReducer` methods are missing. | ||
## Install | ||
@@ -33,6 +43,6 @@ | ||
```js | ||
var chopped = require('chopped-redux') | ||
var createStore = require('chopped-redux') | ||
function reducer (state, action) { | ||
state = state || 0 // initialize state if empty | ||
function update (state, action) { | ||
state = state || 0 // always initialize state if empty | ||
@@ -46,3 +56,3 @@ if (action.type === 'increment') { | ||
var store = chopped(reducer) | ||
var store = createStore(update) | ||
var action = { type: 'increment' } // actions are objects | ||
@@ -61,5 +71,6 @@ | ||
- All state of your app goes into `state`, a single object | ||
- The `reducer` function is **pure** (it should *only* update and return new `state` and nothing else) | ||
- `actions` are plain objects with at least two properties `type` (String) and `payload` (Mixed) | ||
- You do async inside helper functions [(action dispatchers)](#async-and-action-creators) that call `dispatch` when done | ||
- The `update` function is **pure** (it should *only* update and return new `state` and nothing else) | ||
- The `update` function always return some initial state if undefined. | ||
- `actions` are plain objects with at least one property: `type` (String), and an optional `payload` (Mixed). | ||
- You do async inside functions [(action dispatchers)](#async-and-action-creators) that call `dispatch` when done | ||
@@ -79,11 +90,10 @@ ## API | ||
The factory has a single mandatory param which is a `reducer` function. | ||
The factory has a single mandatory param which is a `update` function. | ||
#### `createStore(reducer[, initialState, listeners])` | ||
#### `createStore(update[, initialState])` | ||
- *reducer* `Function` | ||
- *update* `Function` | ||
- *initialState* `Mixed` Anything you want to hold your state in | ||
- *listeners* `Array` Listener callbacks that subscribe to dispatches | ||
The `reducer` function should have the following signature: | ||
The `update` function should have the following signature: | ||
@@ -101,3 +111,3 @@ ```js | ||
```js | ||
state = reducer(state, action) | ||
state = update(state, action) | ||
``` | ||
@@ -128,23 +138,7 @@ | ||
### Helpers | ||
#### `wrap(methods, dispatch)` | ||
Available at `require('chopped-redux/wrap')`. | ||
This is a highly opinionated helper that binds your action dispatchers (aka action creators) to a `store.dispatch` instance, by currying them. | ||
This functions are meant to have this signature `function (dispatch, payload) {}`. See [Async and action creators](#async-and-action-creators) below. | ||
- Returns `Object` The same methods wrapping the dispatcher | ||
- *methods* `Object` An object with your action dispatcher functions | ||
- *dispatch* `Function` The `dispatch` method from your `store` instance | ||
--- | ||
## Async and action creators | ||
Handling async stuff in vanilla Flux is a pain. In the beginning of Flux we were making API calls inside our Stores, that turned out to be a bad idea. So they came up with this pompous concept of Action Creators to confuse us all (at least for a while). [If you’re still confused, Action Creators are functions that return Actions, which are simply objects; so Action == plain object, Action Creator == function that creates an Action.] Apparently no-one knows how to do this right. | ||
Handling async stuff in vanilla Flux is a pain. In the beginning of Flux we were making API calls inside our Stores, that turned out to be a bad idea. So they came up with this pompous concept of Action Creators to confuse us all (at least for a while). [If you’re still confused, Action Creators are functions that return Actions, which are simply objects; so Action == plain object; Action Creator == function that creates an Action object.] Apparently no-one knows how to do this right. | ||
In Redux there’s middleware. The [thunk](https://github.com/gaearon/redux-thunk) middleware *transforms* an Action Creator (they call it “intent”) into an object that you can dispatch, and you *create* Action Creators like this: | ||
In Redux there’s middleware. The [thunk](https://github.com/gaearon/redux-thunk) middleware allows you to literally dispatch a function, and your Action Creators look like this: | ||
@@ -169,3 +163,3 @@ ```js | ||
I prefer to (partly) avoid the concept of Action Creators with a simpler approach, namely this: | ||
I prefer a simpler and more explicit approach based only on the necessity of delaying the dispatch, namely this: | ||
@@ -185,5 +179,5 @@ ```js | ||
in which the `dispatch` callback always gets passed in as first argument. | ||
a function in which the `dispatch` callback always gets passed-in as first argument. You could also pass in the very `store` instance if you need to `getState()`. | ||
If you care about names, I would call this an *action dispatcher* function, because that’s what it does. There’s no nesting, no type checking, no complexity. You just pass in a callback for dispatching an action with some payload. You’re just delegating `dispatch`ing actions to a helper function to do some things before the dispatch. | ||
I would call this an *action dispatcher* function, because that’s what it does. There’s no nesting, no type checking, no complexity. You just pass in a callback for dispatching an action with some payload. You’re just delegating `dispatch`ing actions to a helper function to do some things before the dispatch. | ||
@@ -202,3 +196,3 @@ If you don’t need async, simply `dispatch` the action directly and you’ve got one less function to care about. | ||
https://gist.github.com/vslinko/cab24085f029def8997b by @vslinko | ||
A [gist](https://gist.github.com/vslinko/cab24085f029def8997b) by [@vslinko](http://github.com/vslinko) | ||
[The Evolution of Flux Frameworks](https://medium.com/@dan_abramov/the-evolution-of-flux-frameworks-6c16ad26bb31) | ||
@@ -205,0 +199,0 @@ |
@@ -6,3 +6,2 @@ | ||
var createStore = require('../') | ||
var wrap = require('../wrap') | ||
@@ -13,3 +12,3 @@ // Action types | ||
// Action factories (creators) | ||
// Action helpers | ||
var increment = function (dispatch) { | ||
@@ -26,4 +25,4 @@ dispatch({ type: INCREMENT_COUNTER }) | ||
// Reducers | ||
var reducer = function (state, action) { | ||
// Updaters | ||
var update = function (state, action) { | ||
state = state || { counter: 10 } | ||
@@ -42,3 +41,3 @@ | ||
} | ||
var immutableReducer = function (state, action) { | ||
var immutableUpdate = function (state, action) { | ||
switch (action.type) { | ||
@@ -56,3 +55,3 @@ case INCREMENT_COUNTER: | ||
test('factory', function (t) { | ||
test('Factory', function (t) { | ||
t.plan(2) | ||
@@ -66,3 +65,3 @@ | ||
createStore() | ||
}, 'throws if missing reducer param') | ||
}, 'throws if missing update param') | ||
@@ -72,26 +71,30 @@ t.notEqual(a, b, 'no singleton') | ||
test('mutable, listeners', function (t) { | ||
t.plan(4) | ||
test('Dispatching', function (t) { | ||
t.plan(2) | ||
var store = createStore(reducer, state) | ||
var store = createStore(update) | ||
t.doesNotThrow(store.dispatch, 'empty is possible') | ||
store.subscribe(function () {}) | ||
var unsubscribe = store.subscribe(function () { t.pass('listener called') }) | ||
store.subscribe(function () {}) | ||
var result = store.dispatch({ foo: 'bar' }) | ||
t.equal(result.foo, 'bar', 'returns given action') | ||
}) | ||
test('Mutable', function (t) { | ||
t.plan(3) | ||
var store = createStore(update, state) | ||
increment(store.dispatch) | ||
t.equal(store.getState().counter, 2, 'action dispatched 1') | ||
unsubscribe() | ||
decrement(store.dispatch) | ||
t.equal(store.getState().counter, 1, 'action dispatched 1') | ||
t.equal(store.getState().counter, 1, 'action dispatched 2') | ||
t.equal(store.getState(), state, 'state is the same mutable object') | ||
t.equal(store.getState(), state, 'state is the same (mutable) object') | ||
}) | ||
test('immutable', function (t) { | ||
test('Immutable', function (t) { | ||
t.plan(3) | ||
var store = createStore(immutableReducer, immutableState) | ||
var store = createStore(immutableUpdate, immutableState) | ||
@@ -102,3 +105,3 @@ increment(store.dispatch) | ||
decrement(store.dispatch) | ||
t.equal(store.getState().get('counter'), 1, 'action dispatched 1') | ||
t.equal(store.getState().get('counter'), 1, 'action dispatched 2') | ||
@@ -108,16 +111,37 @@ t.notEqual(store.getState(), immutableState, 'state is not the same object') | ||
test('no initial state provided', function (t) { | ||
test('Listeners', function (t) { | ||
t.plan(5) | ||
var store = createStore(immutableUpdate, Immutable.Map({ counter: 5 })) | ||
var off1 = store.subscribe(function () { t.pass('get called 1 (x2)') }) | ||
var off2 = store.subscribe(function () { | ||
t.equal(store.getState().get('counter'), 6, 'can be remove from within (x1)') | ||
off2() | ||
}) | ||
var off3 = store.subscribe(function () { t.pass('get called 2 (x2)') }) | ||
store.dispatch({ type: INCREMENT_COUNTER }) | ||
store.dispatch() | ||
off1() | ||
off3() | ||
store.dispatch() | ||
}) | ||
test('No initial state provided', function (t) { | ||
t.plan(1) | ||
var store = createStore(reducer, null) | ||
var store = createStore(update, null) | ||
store.dispatch({ type: DECREMENT_COUNTER }) | ||
t.equal(store.getState().counter, 9, 'gets set in reducer') | ||
t.equal(store.getState().counter, 9, 'gets set by updater') | ||
}) | ||
test('first-class dispatch and getState, no bind', function (t) { | ||
test('First-class dispatch and getState, no bind', function (t) { | ||
t.plan(2) | ||
var initialState = { counter: 5 } | ||
var store = createStore(reducer, initialState) | ||
var store = createStore(update, initialState) | ||
@@ -133,6 +157,6 @@ function wrapper (fn) { | ||
t.equal(getState(), initialState, 'getState') | ||
t.equal(getState(), initialState, 'for getState') | ||
increment(dispatch) | ||
t.equal(getState().counter, 6, 'dispatch') | ||
t.equal(getState().counter, 6, 'for dispatch') | ||
}) | ||
@@ -144,3 +168,3 @@ | ||
var initialState = { counter: -1 } | ||
var store = createStore(reducer, initialState) | ||
var store = createStore(update, initialState) | ||
@@ -155,29 +179,17 @@ increment(store.dispatch) | ||
test('wrap/curry action factories', function (t) { | ||
test('`updater` property', function (t) { | ||
t.plan(3) | ||
var inc = function (dispatch, data) { | ||
t.equal(data.foo, 'bar', 'arguments get passed in') | ||
var store = createStore(update) | ||
dispatch({ | ||
type: INCREMENT_COUNTER | ||
}) | ||
} | ||
store.dispatch() | ||
t.equal(store.getState().counter, 10) | ||
var store = createStore(reducer, { counter: 20 }) | ||
var actions = wrap({ increment: inc }, store.dispatch) | ||
t.equal(typeof store.updater, 'function', 'is getter') | ||
actions.increment({ foo: 'bar' }) | ||
t.equal(store.getState().counter, 21, 'works') | ||
t.throws(function () { | ||
wrap({ increment: increment }, store.dispatch) | ||
}, 'must take at least 2 arguments') | ||
store.updater = function (state, action) { | ||
return { counter: 99 } | ||
} | ||
store.dispatch() | ||
t.equal(store.getState().counter, 99, 'is setter') | ||
}) | ||
test('empty dispatching', function (t) { | ||
t.plan(1) | ||
var store = createStore(reducer) | ||
t.doesNotThrow(store.dispatch, 'is possible') | ||
}) |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
0
0
12580
5
170
193
- Removedcurry@^1.2.0
- Removedcurry@1.2.0(transitive)