https://github.com/thk2b/redux-structures
Reusable redux data structures
https://github.com/thk2b/redux-structures
Last synced: about 1 year ago
JSON representation
Reusable redux data structures
- Host: GitHub
- URL: https://github.com/thk2b/redux-structures
- Owner: thk2b
- License: mit
- Created: 2018-04-13T15:08:33.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2018-05-15T23:16:10.000Z (about 8 years ago)
- Last Synced: 2025-05-09T00:58:36.972Z (about 1 year ago)
- Language: JavaScript
- Size: 68.4 KB
- Stars: 6
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# redux-strucures
Reusable idiomatic redux data structures.
[](https://badge.fury.io/js/redux-structures)
[](https://github.com/ellerbrock/open-source-badge/)
```
npm install --save redux-structures
```
## Docs
- [Value](https://github.com/thk2b/redux-structures/blob/master/src/Value/docs.md)
- [HashMap](https://github.com/thk2b/redux-structures/blob/master/src/HashMap/docs.md)
- [List](https://github.com/thk2b/redux-structures/blob/master/src/List/docs.md)
## Motivation
Redux applications often implement the same reducer logic. For instance, adding or removing a property from a state object, inserting an element to a list or updating a value. `redux-structures` provides reusable implementations of these common data structures so that you do not have to rewrite the same reducers over and over.
To illustrate the problem, consider a chat application. The store has `users` and `messages`, which are both objects:
```js
store.getState()
/*
{
users: {
1: { id: 1, name: 'john doe', ... }
2: { id: 2, name: 'jane doe', ... }
...
},
messages: {
1: { id: 1, text: 'hello, world', userId: 1, ...}
2: { id: 2, text: 'what\'s up ?', userId: 2, ...},
...
}
}
*/
```
Common actions include adding or removing a user, as well as adding or removing a message.
```js
function users(state, action){
switch(action.type){
case ADD_USER: return {...state, [action.user.id]: action.user}
...
}
}
function messages(state, action){
switch(action.type){
case ADD_MESSAGE: return {...state, [action.message.id]: action.message}
...
}
}
```
Notice that the only difference between the two reducers are the constants and the action property names. The same error-prone logic is repeated twice.
`redux-structures` implements this logic *once*, and allows you to instantly create coupled reducers and actions.
```js
import { HashMap } from 'redux-structures'
const { reducer: users, actions: userActions} = HashMap('users')
const { reducer: messages, actions: messageActions} = HashMap('messages')
```
Now, to add a user, simply dispatch `userActions.set(id, user)`. (See the documentation for more information)
This has several advantages.
- Faster and safer development
- You no longer need to unit-test individual reducers, since they tested once at the library level.
## Concepts
`redux-structures` employs two core concepts.
- Structures
Structures are functions that return a reducer and actions. There are different types of structures: `Value`, `HashMap`, and `List`.
- Instances
Instances are reducer - actions pairs, obtained by calling a structure. Instances are created with a unique name, so that actions only affect the reducer to which they are bound, and not reducers from other instances of the same structure. Actions and reducers are coupled: reducers only match actions created by the instance's action creators. In our example, an instance would be `messages` and `messagesAction`.
## Patterns
`redux-structures` provides basic level operations, and does not get in the way of your application-specific needs. You can dispatch the actions from anywhere in your app - from the view, middleware, or thunk like you do with traditional actions.
### Higher order action creators
You can define higher-order actions, which take a parameter and return another action creator. Consider what happens a user submits a new message, in the earlier example.
```js
const { reducer: messages, actions: messageActions} = HashMap('messages')
const createMessage = text => {
const id = generateId()
const message = {
text,
sentAt: Date.now(),
id
}
return messageActions.set(id, message)
}
```
Here, the `create` action returns another more general action creator.
### Using middleware
Actions from `redux-structures` can be dispatched from anywhere, including middleware.
### Composing reducers
You can define reducers which contain reducers from `redux-structures`.
## Usage
It is recomended to export instances from their own module, like in traditional redux applications. Then, import all reducers when creating the store, and import actions where necesary.
`messages.js`
```js
import { HashMap } from 'redux-structures'
const { reducer, actions } = HashMap('messages')
/* define custom actions, in this case with the thunk middleware */
function fetchMessage(id){
return dispatch => {
fetch(`/message/${id}`)
.then(message => dispatch(actions.set(id, message)))
}
}
function fetchMessages(){
return dispatch => {
fetch(`/messages`)
.then(messages => {
dispatch(actions.setAll(messages))
})
}
}
const messageActions = {
...actions,
fetchMessage,
fetchMessages
}
export { messageActions }
export default reducer
```
`index.js`
```js
import { createStore, combineReducers } from 'redux'
import messages from './path/to/messages'
import users from './path/to/users'
export default createStore(
combineReducers({
messages,
users
})
)
```
`view.js`
```js
import { messageActions } from './path/to/messages'
/* import actions and dispatch as you wish */
```