Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

chopped-redux

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chopped-redux - npm Package Compare versions

Comparing version 2.0.1 to 3.0.0

index.js

16

package.json
{
"name": "chopped-redux",
"version": "2.0.1",
"description": "A tiny Flux implementation based on @gaearon Redux",
"version": "3.0.0",
"description": "A subset of @gaearon Redux",
"keywords": [
"flux",
"redux"
"redux",
"state"
],
"homepage": "https://github.com/acstll/chopped-redux",
"main": "src/",
"main": "index.js",
"author": "acstll <arturo@arturu.com>",

@@ -18,3 +19,5 @@ "repository": "acstll/chopped-redux",

"license": "MIT",
"dependencies": {},
"dependencies": {
"curry": "^1.2.0"
},
"devDependencies": {

@@ -26,5 +29,6 @@ "immutable": "^3.7.4",

"scripts": {
"test": "npm run lint && node test/index.js",
"test": "node test/index.js",
"pretest": "npm run lint",
"lint": "standard"
}
}
# Chopped Redux
A very small Flux implementation based on [@gaearon](https://github.com/gaearon) [Redux](https://github.com/gaearon/redux), mainly inspired by [this](https://github.com/gaearon/redux/pull/166) and [this](https://github.com/gaearon/redux/issues/113#issuecomment-114049804).
![npm version](https://img.shields.io/npm/v/chopped-redux.svg) ![npm downloads](https://img.shields.io/npm/dm/chopped-redux.svg)
The idea here is to provide a minimal, solid Flux (à la Redux) base without the [React](http://facebook.github.io/react/index.html) glue (you have to do that yourself), so it's possible to use this library with anything other than React. [Redux](https://github.com/gaearon/redux) allows you to do this too in 1.0, by [splitting itself](https://github.com/gaearon/redux/issues/230).
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".
You should be able to switch from Chopped Redux to Redux and viceversa without changing your flux code (constants, action creators and reducers).
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).
## Bullet points
This project follows [SemVer](http://semver.org/).
Chopped main design goal:
## Motivation
- All methods are first-class. You can freely pass them around without the need for `bind`ing.
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**.
Why is Flux Redux-style so nice:
## Install
- No singletons
- Action creators and reducers (state-less stores) are pure functions
- All state is kept in a single object, and you choose what that is (Immutable, mori, a plain object)
- Plays well with Universal Javascript
With [`npm`](http://npmjs.org) do:
The things you'll miss from Redux here:
```bash
npm install chopped-redux --save
```
- Hot reloading
- Middleware
- Built-in time-travel
## Usage
This project follows [SemVer](http://semver.org/).
This is how it works:
## Install
- You `dispatch` an `action`
- The `state` gets updated based on that `action`
- All `listeners` get notified of the `state` change
With [`npm`](http://npmjs.org) do:
```js
var chopped = require('chopped-redux')
```bash
npm install chopped-redux --save
function reducer (state, action) {
state = state || 0 // initialize state if empty
if (action.type === 'increment') {
return state + 1
}
return state // always return state
}
var store = chopped(reducer)
var action = { type: 'increment' } // actions are objects
store.subscribe(function () {
console.log(store.getState())
})
store.dispatch(action)
// => 1
```
Guidelines for success:
- 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
## API
```js
var factory = require('chopped-redux')
var createStore = require('chopped-redux')
```
Chopped Redux exports a single factory function that returns an object with three main methods:
Chopped Redux exports a single factory function that returns an object with four methods:

@@ -49,13 +73,7 @@ - `dispatch`

- `subscribe`
and two helpers:
- `wrap`
- `replaceState`
I like to call this instance object `flux`, in Redux is called the `store`.
The factory has a single mandatory param which is a `reducer` function.
#### factory(reducer[, initialState, listeners])
#### `createStore(reducer[, initialState, listeners])`

@@ -66,3 +84,3 @@ - *reducer* `Function`

The `reducer` function should have the folowwing signature:
The `reducer` function should have the following signature:

@@ -85,76 +103,96 @@ ```js

#### #dispatch(action)
#### `dispatch(action)`
- *action* `Object|Function`
- Returns `undefined`
- *action* `Object`
Action creators should mostly return a plain object of the `{ type: DO_STUFF }` kind. If you need to do async stuff, return a function instead. This function receives `dispatch` and `getState` as params, so you can then actually `dispatch` the plain object needed for dispatching.
#### `getState()`
```js
var asyncActionCreator = function (data) {
return function (dispatch, getState) {
// do your async stuff
- Returns `Object` The current state
dispatch({
type: STUFF_DONE,
payload: foo
})
}
}
```
#### `subscribe(listener)`
#### #getState()
- Returns `Function` A function to remove the listener
- *listener* `Function` A callback that gets fired after every state update
- Returns `Object` The current state
#### `replaceState(state)`
#### #subscribe(listener)
- Returns `undefined`
- *state* `Mixed` Whatever your state is
- *listener* `Function` A callback that gets fired after every state update
- Returns `Function` A function to remove the listener
This will replace the current state reference in your `store` instance. This could be used for debugging, time-travel, etc. Beware you need to call `dispatch` after replacing the state if you want your views to update or whatever.
---
### Helpers
#### #wrap(methods)
#### `wrap(methods, dispatch)`
- *methods* `Object` An object with your action creators methods
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
So insted of doing this:
---
## 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.
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:
```js
var increment = function () {
return { type: INCREMENT }
function foo (bar) {
// do async stuff
return function (dispatch) {
dispatch({
type: FOO,
bar: bar
})
}
}
flux.dispatch(increment())
// after binding it and what not, call it
foo()
```
you can do this:
I prefer to (partly) avoid the concept of Action Creators with a simpler approach, namely this:
```js
var actions = flux.wrap({ increment: increment })
function foo (dispatch, payload) {
// do async stuff
actions.increment()
dispatch({
type: FOO,
payload: payload
})
}
foo(store.dispatch, { foo: ‘bar’ })
```
The nice thing about this is that you can provide you view components with this wrapped action creator methods which you can call directly without needing the `flux` instance available.
in which the `dispatch` callback always gets passed in as first argument.
#### #replaceState(state)
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.
- *state* `Mixed` Whatever your state is
If you don’t need async, simply `dispatch` the action directly and you’ve got one less function to care about.
This will replace the current state reference in your `flux` instance. This could be used for debugging, time-travel. For example, you could keep a copy of your `state` object of a specific point in time, and restore it later.
```js
// Copy of current state
var stateCopy = flux.getState()
store.dispatch({ type: FOO, payload: payload })
```
// Do stuff
If you want to be consistent, go always the async way no matter what.
// Some time later
flux.replaceState(stateCopy)
```
No more `ActionCreators.addTodo(text)`.
---
## Further reading
Further reading: [The Evolution of Flux Frameworks](https://medium.com/@dan_abramov/the-evolution-of-flux-frameworks-6c16ad26bb31).
https://gist.github.com/vslinko/cab24085f029def8997b by @vslinko
[The Evolution of Flux Frameworks](https://medium.com/@dan_abramov/the-evolution-of-flux-frameworks-6c16ad26bb31)

@@ -161,0 +199,0 @@ ## License

@@ -5,18 +5,15 @@

var fluxFactory = require('../')
var createStore = require('../')
var wrap = require('../wrap')
// Silly constants
// Action types
var INCREMENT_COUNTER = 'INCREMENT_COUNTER'
var DECREMENT_COUNTER = 'DECREMENT_COUNTER'
// Actions
var increment = function () {
return {
type: INCREMENT_COUNTER
}
// Action factories (creators)
var increment = function (dispatch) {
dispatch({ type: INCREMENT_COUNTER })
}
var decrement = function () {
return {
type: DECREMENT_COUNTER
}
var decrement = function (dispatch) {
dispatch({ type: DECREMENT_COUNTER })
}

@@ -60,7 +57,7 @@

var identity = function (x) { return x }
var a = fluxFactory(identity)
var b = fluxFactory(identity)
var a = createStore(identity)
var b = createStore(identity)
t.throws(function () {
fluxFactory()
createStore()
}, 'throws if missing reducer param')

@@ -74,17 +71,17 @@

var flux = fluxFactory(reducer, state)
var store = createStore(reducer, state)
flux.subscribe(function () {})
var unsubscribe = flux.subscribe(function () { t.pass('listener called') })
flux.subscribe(function () {})
store.subscribe(function () {})
var unsubscribe = store.subscribe(function () { t.pass('listener called') })
store.subscribe(function () {})
flux.dispatch(increment())
t.equal(flux.getState().counter, 2, 'action dispatched 1')
increment(store.dispatch)
t.equal(store.getState().counter, 2, 'action dispatched 1')
unsubscribe()
flux.dispatch(decrement())
t.equal(flux.getState().counter, 1, 'action dispatched 1')
decrement(store.dispatch)
t.equal(store.getState().counter, 1, 'action dispatched 1')
t.equal(flux.getState(), state, 'state is the same mutable object')
t.equal(store.getState(), state, 'state is the same mutable object')
})

@@ -95,11 +92,11 @@

var flux = fluxFactory(immutableReducer, immutableState)
var store = createStore(immutableReducer, immutableState)
flux.dispatch(increment())
t.equal(flux.getState().get('counter'), 2, 'action dispatched 1')
increment(store.dispatch)
t.equal(store.getState().get('counter'), 2, 'action dispatched 1')
flux.dispatch(decrement())
t.equal(flux.getState().get('counter'), 1, 'action dispatched 1')
decrement(store.dispatch)
t.equal(store.getState().get('counter'), 1, 'action dispatched 1')
t.notEqual(flux.getState(), immutableState, 'state is not the same object')
t.notEqual(store.getState(), immutableState, 'state is not the same object')
})

@@ -110,6 +107,6 @@

var flux = fluxFactory(reducer, null)
var store = createStore(reducer, null)
flux.dispatch(decrement())
t.equal(flux.getState().counter, 9, 'gets set in reducer')
store.dispatch({ type: DECREMENT_COUNTER })
t.equal(store.getState().counter, 9, 'gets set in reducer')
})

@@ -121,3 +118,3 @@

var initialState = { counter: 5 }
var flux = fluxFactory(reducer, initialState)
var store = createStore(reducer, initialState)

@@ -130,24 +127,11 @@ function wrapper (fn) {

var dispatch = wrapper(flux.dispatch)
var getState = wrapper(flux.getState)
var dispatch = wrapper(store.dispatch)
var getState = wrapper(store.getState)
t.equal(getState(), initialState, 'getState')
dispatch(increment())
increment(dispatch)
t.equal(getState().counter, 6, 'dispatch')
})
test('handle actions being functions', function (t) {
t.plan(2)
var flux = fluxFactory(reducer, { counter: 32 })
flux.dispatch(function (dispatch, getState) {
t.equal(getState().counter, 32, 'getState gets passed in')
dispatch({ type: INCREMENT_COUNTER })
})
t.equal(flux.getState().counter, 33, 'alright')
})
test('replaceState', function (t) {

@@ -157,31 +141,32 @@ t.plan(2)

var initialState = { counter: -1 }
var flux = fluxFactory(reducer, initialState)
var store = createStore(reducer, initialState)
flux.dispatch(increment())
t.equal(flux.getState().counter, 0, '(test dispatch)')
increment(store.dispatch)
t.equal(store.getState().counter, 0, '(test dispatch)')
flux.replaceState({ counter: 24 })
flux.dispatch(decrement())
t.equal(flux.getState().counter, 23, 'works')
store.replaceState({ counter: 24 })
decrement(store.dispatch)
t.equal(store.getState().counter, 23, 'works')
})
test('wrap action creators', function (t) {
test('wrap/curry action factories', function (t) {
t.plan(3)
var inc = function (data) {
var inc = function (dispatch, data) {
t.equal(data.foo, 'bar', 'arguments get passed in')
return {
dispatch({
type: INCREMENT_COUNTER
}
})
}
var flux = fluxFactory(reducer, { counter: 20 })
var actions = flux.wrap({ increment: inc, decrement: decrement })
var store = createStore(reducer, { counter: 20 })
var actions = wrap({ increment: inc }, store.dispatch)
actions.increment({ foo: 'bar' })
t.equal(flux.getState().counter, 21, 'works')
t.equal(store.getState().counter, 21, 'works')
actions.decrement()
t.equal(flux.getState().counter, 20, 'correctly')
t.throws(function () {
wrap({ increment: increment }, store.dispatch)
}, 'must take at least 2 arguments')
})

@@ -192,4 +177,4 @@

var flux = fluxFactory(reducer)
t.doesNotThrow(flux.dispatch, 'is possible')
var store = createStore(reducer)
t.doesNotThrow(store.dispatch, 'is possible')
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc