Models layer for Redux. redux-models
simplifies working with remote data (well.. not only remote) and helps to organize your code.
npm install --save redux redux-models
import { createModel } from 'redux-models';
export default createModel({
name: 'User',
methods: {
findByUsername(username) {
return fetch(`https://api.github.com/users/${username}`).then(res =>
res.json()
);
}
}
});
import { combineReducers, applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import User from './models/User';
export default createStore(
combineReducers({
...User.reducers
}),
applyMiddleware(thunk)
);
import React from 'react';
import { connect } from 'react-redux';
import User from './models/User';
class UserAvatar extends React.Component {
componentDidMount() {
const { fetchUser } = this.props;
fetchUser();
}
render() {
const { user } = this.props;
if (!user) {
return <div>Loading...</div>;
}
return <img src={user.avatar_url} alt="avatar" />;
}
}
export default connect(
(state, { username }) => ({
user: User(state).findByUsername(username)
}),
(dispatch, { username }) => ({
fetchUser: () => dispatch(User.findByUsername(username))
})
)(UserAvatar);
options
:
options.name
: (String
): Name of a modeloptions.mixins
: (Array
): Array of mixinsoptions.methods
: (Object
): Model's methodsoptions.reducer
: (Function
[optional]): Model reducer.options.typePrefix
: (String
[optional]): Prefix of actions types. Default@@redux-models
.options.modelState
: (Function
[optional]): Function to map state to model state. Defaultstate => state[{ model name }]
.
Newly created model with defined methods. Each model method creates action to dispatch.
Additional data processing from the methods can be done in the model reducer.
Model reducer arguments are same as redux reducers, except the last argument types
.
It contains all action types strings your model can dispatch (including mixins action types).
In following example model User
has one method find
and it can dispatch actions with types: @@redux-models/USER/FIND
, @@redux-models/USER/FIND_SUCCESS
, @@redux-models/USER/FIND_ERROR
, @@redux-models/USER/FIND_RESET
,
so types
contains object:
{
FIND: '@@redux-models/USER/FIND',
find: '@@redux-models/USER/FIND',
FIND_SUCCESS: '@@redux-models/USER/FIND_SUCCESS',
findSuccess: '@@redux-models/USER/FIND_SUCCESS',
FIND_ERROR: '@@redux-models/USER/FIND_ERROR',
findError: '@@redux-models/USER/FIND_ERROR',
FIND_RESET: '@@redux-models/USER/FIND_RESET',
findReset: '@@redux-models/USER/FIND_RESET'
}
After processing, the data will be available in state.{ model name }.model
.
import { createModel } from 'redux-models';
const defaultState = {
byId: {}
};
export default createModel({
name: 'User',
methods: {
find(query) {
// async request
}
},
reducer(state = defaultState, action, { findSuccess }) {
const { type, payload: { result } = {} } = action;
if (type === findSuccess) {
return {
...state,
byId: {
...state.byId,
...(result || []).reduce(
(byId, user) => ({
...byId,
[user.id]: user
}),
{}
)
}
};
}
return state;
}
});
Then:
import { connect } from 'react-redux';
// ...
export default connect((state, { id }) => ({
user: state.User.model.byId[id]
}))(UserCard);
Mixins allow you to add method sets to multiple models. For example mixin
crud
adds methods: create
, updateById
, deleteById
, find
, findById
.
import createMixin from 'redux-models-mixin-crud';
export default function crudMixin(path) {
return createMixin({
methods: {
create() { /*...*/ },
updateById() { /*...*/ },
deleteById() { /*...*/ },
find() { /*...*/ },
findById() { /*...*/ }
}
});
}
import { createModel } from 'redux-models';
import crudMixin from './crud';
export default createModel({
name: 'Book',
mixins: [crudMixin('/books')]
});
See the Contributors Guide
License MIT