redux-rest
NOTE this requires redux 1.0.0-rc or above
NOTE POST/PUT requests currently tied to Django Rest Framework's CSRF handling and response content
Create Redux action constants, action creators and reducers for your REST API with no boilerplate.
Install
npm install redux-rest
Example
import React from 'react';
import { connect, Provider } from 'react-redux';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import API from 'redux-rest';
// This is a super simple app that displays a list of users from an API and
// lets you add new users. Until a success response is recevied from the API
// endpoint new users are show as pending.
// To create a store with redux for this app all you have to do is
// describe your API endpoints as key value pairs where the key is an
// identifier and the value is the URL of the endpoint.
const myAPI = {
users: '/api/users/'
};
// Then create an API instance. This automatically creates
// action creators and reducers for each endpoint. No boilerplate!
const api = new API(myAPI);
// UserApp uses the api object to fetch the users and create new ones
// using the automatically created action creators.
class UserApp extends React.component {
componentDidMount() {
// Request the list of users when this component mounts
this.props.dispatch(api.actionCreators.users.list());
}
render() {
let users = this.props.users;
let pendingUsers = users.filter(u => u.status === 'pending');
let currentUsers = users.filter(u => u.status !== 'pending');
return (
<div>
{pendingUsers.map(user => <p>Saving {user.username}...</p>)}
<ul>
{currentUsers.map(user => <li>{user.username}</li>)}
</ul>
<input ref="username" placeholder="Enter username"/>
<input type="submit" value="Add user" onClick={this._addUser}/>
</div>
);
}
_addUser() {
let inputNode = React.findDOMNode(this.refs.username);
let val = inputNode.value;
this.props.dispatch(
api.actionCreators.users.create(
{username: val}
)
);
inputNode.val = '';
}
}
// The api object also has reducers to handle the standard REST actions
// So we can configure redux and connect our UserApp to it.
let reducers = combineReducers(api.reducers);
// To integrate with redux we require the thunk middleware to handle
// action creators that return functions.
let createStoreWithMiddleware = applyMiddleware(
thunkMiddleware
)(createStore);
let store = createStoreWithMiddleware(reducers);
// Which props do we want to inject, given the global state?
function select(state) {
// Each endpoint has an _items and _collection reducer. Here we only need
// the user items so we only pull out users_items.
return {
users: state.users_items
};
})
// Wrap UserApp to inject dispatch and state into it
UserApp = connect(select)(UserApp);
export default class App extends React.Component {
render() {
// To render UserApp we need to wrap it in redux's Provider.
return (
<Provider store={store}>
{() => <UserApp />}
</Provider>
);
}
}
What is the api object?
The api object encapsulates common patterns when dealing with REST APIs.
When created with a description of your API you can call all the actions you'd expect and there are reducers that automatically handle those actions, including 'pending', 'success' and 'failure' states.
import API from 'redux-rest';
const myAPI = {
users: '/api/users/',
}
const api = new API(myAPI);
This creates a pair of reducers for each API endpoint; a collection reducer to handle actions at the collection level and and item reducer to handle actions on individual items.
TODO not sure about the item/collection stuff. Needs a rethink.
Calling actions is as simple as
api.actionCreators.users.create(userData);
Status of API requests
Each action creator triggers an API request and immediately dispatches an action so the UI can reflect the change straight away. During the request the state change is marked as pending. For example, creating a new object,
api.actionCreators.users.create({username: 'mark'});
will add,
{
username: 'mark',
status: 'pending'
}
to the state.
TODO what if 'status' is already a field of user?
On completion of the request the status is updated to saved
or
failed
as appropriate. E.g.
{
username: 'mark',
status: 'saved'
}
Available actions
The standard set of REST actions is available; list
,
retrieve
, create
and update
.
TODO
- add
delete
action - add a
revert
action to revert optimistic changes if API request fails. - support APIs with custom endpoints
- integrate normalizr for flat object storage?