duckfactory
Advanced tools
Comparing version 1.3.0 to 1.3.1
{ | ||
"name": "duckfactory", | ||
"version": "1.3.0", | ||
"version": "1.3.1", | ||
"description": "Factory for creating redux ducks", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
198
README.md
# Duckfactory | ||
**Simple creation and use of redux ducks.** | ||
**Redux ducks, even simpler** | ||
[Redux](https://github.com/reactjs/redux) / [react redux](https://github.com/reactjs/react-redux) are great, | ||
but can produce a lot of boilerplate and fragmented code as a project grows, even for simple functionality. | ||
[Redux](https://github.com/reactjs/redux) / [react redux](https://github.com/reactjs/react-redux) are pretty great, but as the code grows, things easily get cumbersome, complex and fragmented. [Redux ducks](https://github.com/erikras/ducks-modular-redux) is an interesting proposal for handling this: organizing the code in bundles of action creators, action types and reducers that belong together, in the very common cases where there's a one-to-one (-to-one) relationship between them. | ||
I really like the concept of [redux ducks](https://github.com/erikras/ducks-modular-redux), | ||
as it aims to simplify and structure this: | ||
Bundle the reducers with the actions that trigger them when they belong together - which they do most of the time. | ||
Duckfactory is a library for wrapping and simplifying this even more. It auto-generates action creators and action types and encapsulates them, only exposing them when needed for side purposes like unit testing. A lot of the boilerplate is abstracted away, the project code gets more minimal, clean and readable, and you, the brilliant developer, gets happier. | ||
Duckfactory tries to make it as easy as possible to create ducks and add functionality to a project if necessary, | ||
while keeping the project code minimal, clean and readable. | ||
## How does it work? | ||
## Installation | ||
In short, give it a prefix string to group the actions, an initial state, an object with the names of action creators and the reducers the actions should trigger. It will then create an object that exposes ordinary redux action creators, reducers and action types. | ||
### Example | ||
Let's define a reducer in an example file `users.js`: | ||
```javascript | ||
import DuckFactory from 'duckfactory'; | ||
// Just an example initial redux state (userId → userObject): | ||
const initialState = { | ||
42: { | ||
name: "Arthurarthurdent", | ||
age: "30-ish", | ||
occupation: "Sandwich maker" | ||
}, | ||
}; | ||
// Defining two ducks in the collection 'myapp/userducks': | ||
const userDucks = new DuckFactory("myapp/userducks", initialState, { | ||
setName: (state, {userId, newName}) => { | ||
const newUser = {...state[userId]}; | ||
if (!newUser) { | ||
return state; | ||
} | ||
return { | ||
...state, | ||
[userId]: { | ||
...newUser, | ||
name: newName | ||
} | ||
} | ||
}, | ||
addUser: (state, {userId, name, age, occupation}) => { | ||
return { | ||
...state, | ||
[userId]: { name, age, occupation } | ||
} | ||
} | ||
}); | ||
``` | ||
npm install duckfactory --save | ||
About the syntax here: I prefer the ES6 arrow notation and destructuring the action argument. But the more classic syntax like this should also work fine, so you can define it like this instead: | ||
```javascript | ||
///... | ||
setName: function (state, action) { | ||
const newUser = {...state[action.userId]}; | ||
if (!newUser) { | ||
return state; | ||
} | ||
return { | ||
...state, | ||
[action.userId]: { | ||
...newUser, | ||
name: action.newName | ||
} | ||
} | ||
}, | ||
addUser: (state, action) => { | ||
return { | ||
...state, | ||
[action.userId]: { | ||
name: action.name, | ||
age: action.age, | ||
occupation: action.occupation | ||
} | ||
} | ||
} | ||
///... | ||
``` | ||
or | ||
Both of these syntaxes are fine: the action creators are autogenerated in runtime, and the action creator arguments are deduced from the arguments in the reducer functions in the definition. That way, once you've defined it as above, you can get the action creators and use them directly: | ||
```javascript | ||
const actionCreators = userDucks.getActionCreators(); | ||
const action1 = actionCreators.setName("Arthur Dent"); | ||
const action2 = actionCreators.addUser(43, "Marvin", "Will it never end?", "Paranoid android"); | ||
``` | ||
yarn add duckfactory | ||
In this example, these two actions will look like this, ready to dispatch and will trigger regular reducers: | ||
```javascript | ||
console.log(action1); | ||
// { | ||
// type: "myapp/userducks/setName", | ||
// newName: "Arthur Dent" | ||
// } | ||
console.log(action2); | ||
// { | ||
// type: "myapp/userducks/addUser", | ||
// userId: 42, | ||
// name: "Marvin", | ||
// age: "Will it never end?", | ||
// occupation: "Paranoid android", | ||
// } | ||
``` | ||
**NOTE:** if your app uses minification/uglification, version 1.3.0 should be okay, but don't use the versions below. | ||
## How does it work? | ||
### Hooking it up: exporting to combine reducers | ||
Give it a prefix string to group the actions, an initial state, an object with the names of action creators | ||
and the reducers the actions should trigger. | ||
It will then create an object that exposes ordinary redux action creators, reducers and action types. | ||
If you've used redux before, this should be simple. At the bottom of `users.js`, export the necessary: | ||
### Constructor arguments: | ||
- _actionTypePrefix_: a prefix string added before each action creator name, to produce the action type strings. Should be globally unique if you use more than one duckfactory (and/or <a href="https://github.com/espen42/goosefactory">goosefactory</a>). If empty or missing, no prefix will be added and the actioncreator names will be used as action types - then it's up to you to ensure your action types become unique. | ||
- _initialState_: the reducer's initial state (for the state subtree that the duck should cover), | ||
- _actionAndReducerMap_: an object where the keys become the names of action creators, and the values are anonymous functions that become the corresponding reducer to the action creator. The reducer's arguments should be: _state_ as the first argument, and the second argument should be either missing (for reducers that don't require data from the action), or be an action object that is destructured into its keys (reducer argument example: (_state_, {_id_, _name_, _height_}) ). If it's a destructured object, the content of that will become the arguments of the action creator (actioncreator argument example: (_id_, _name_, _height_) ). | ||
- _checkAndWarn_: an optional boolean (default: true) that sets whether to check the duck for consistency: are the arguments correct? Does it produce actionTypes that are globally unique? Serious errors throw Errors, less serious ones only log a warning to the console. | ||
- _logBuilt_: a last optional boolean (default: false) that sets whether to log some details about the produced action creators and actions. Handy for development, no need for it in prod. | ||
```javascript | ||
export default userGeese.getReducers(); | ||
export const actionCreators = userGeese.getActionCreators(); | ||
``` | ||
...and then use the default export in combinereducers as usual, with other duckfactories if you like: | ||
```javascript | ||
import { combineReducers } from 'redux'; | ||
import userReducer from './users'; // <-- This is the default export from above | ||
import gameReducer from './game'; | ||
export default combineReducers({ | ||
game: gameReducer, | ||
users: userReducer | ||
}); | ||
``` | ||
I haven't tried, but I can't see any reason it shouldn't work to mix reducers from duckfactories with reducers that are created in _other_ ways, e.g. regular redux if you need to. You probably want to make sure the action names are all unique, though. | ||
### Constructor: | ||
```javascript | ||
new DuckFactory(actionTypePrefix, initialState, actionAndReducerMap, checkAndWarn, logBuilt) | ||
``` | ||
- `actionTypePrefix`: a prefix string added before each action creator name, to produce the action type strings. I've used the redux-ducks suggestion - `app/reducer` - in the examples, but it's up to you. It should be globally unique if you use more than one duckfactory (and/or [goosefactory](https://github.com/espen42/goosefactory) if you use them together). If empty or missing, no prefix will be added and the actioncreator names will be used as action types - then it's up to you to ensure your action types become unique. A slash is added at the end, in line with the redux-ducks suggestion. | ||
- `initialState`: the reducer's initial state (for the state subtree that the duck should cover), | ||
- `actionAndReducerMap`: an object where the keys become the names of action creators, and the values are anonymous functions that become the corresponding reducer to the action creator. The reducer's arguments are: `state` as the first argument, and the second argument should be either missing (for reducers that don't require data from the action), or be an `action` object - that _may_ be destructured into its keys. | ||
- `checkAndWarn`: an optional boolean (default: `true`) that sets whether to check the duck for consistency: are the arguments correct? Does it produce actionTypes that are globally unique? Serious errors throw Errors, less serious ones only log a warning to the console. | ||
- `logBuilt`: a last optional boolean (default: `false`) that sets whether to log some details about the produced action creators and actions. Handy for development, no need for it in prod. | ||
### Exposed after creation: | ||
After creation, the resulting duck exports as JS objects: | ||
- _.getActionCreators()_: actionCreator-name → actionCreator-function | ||
- _.getReducer()_: actionType → reducer-function | ||
- _.getTypes()_: actionCreator name → actionType | ||
- `.getActionCreators()`: actionCreator-name → actionCreator-function, as described above. | ||
- `.getReducer()`: actionType → reducer-function | ||
- `.getTypes()`: actionCreator name → actionType. (This differs from the original redux-ducks suggestion, in that the types that are exported here don't guarantee the duck convention: `app/reducer/ACTION_TYPE`, since the content here depends on your choice of prefix and the keys in `actionAndReducerMap`. | ||
These are used the same way as ordinary redux actions, action creators and reducers, export the reducer in the regular way to redux or combineReducers. Most of the time, you won't need the exported action types. | ||
## Installation | ||
``` | ||
npm install duckfactory --save | ||
``` | ||
or | ||
``` | ||
yarn add duckfactory | ||
``` | ||
**NOTE:** if your app uses minification/uglification, version 1.3.0 should be okay, but don't use the versions below that. My own testing has been done with webpack 2 and yarn. If your mileage varies, please let me know. | ||
## Examples | ||
...coming soon. Until then, take a look at _src/duckfactory_spec.js_ | ||
## Used with redux-sagas | ||
## Contributions | ||
Suggestions, improvements, corrections, bug notifications, etc... all is welcome on [github](https://github.com/espen42/duckfactory) or _espen42@gmail.com_. Special thanks to **NorwegianKiwi** for awesome help! | ||
The produced actions can of course be used to trigger redux sagas too. | ||
But to make this part even easier, take a look at [Goosefactory](https://github.com/espen42/goosefactory) — a sibling | ||
library made for redux-sagas instead of reducers. Duckfactories and Goosefactories work nicely with each other. | ||
## Using it with redux-sagas | ||
The produced actions can of course be used to trigger [redux-sagas](https://github.com/redux-saga/redux-saga) too. But duckfactory can only use reducer functions, not saga generators. Luckily, here's a sibling library that does the same thing for sagas: [Goosefactory](https://github.com/espen42/goosefactory). Duckfactories and Goosefactories play nicely with each other. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21865
188