redux-supermodel
Streamline the effort it takes for you to communicate between your Redux Store and a REST-like API. This is a package of action creator functions and reducers built with axios and redux-promise-middleware that handle the resource state management for you... all you need is a URL!
Creating the Client
Clients encapsulate the API you are consuming. You can create a new client by providing the base URL for the API.
import { createClient } from 'redux-supermodel'
const client = createClient('https://example.com/api/v1')
Creating Resources
Within your client, you can start defining resources. Each Resource represents an endpoint that you can interact with.
const blogs = client('blogs')
const comments = client('comments')
Attaching your Resource's props to your Component
Start with your resource definition, let's pretend http://example.com/api/posts/latest
will return a JSON object with properties title
and body
.
import { createClient } from 'redux-supermodel'
const client = createClient('http://example.com/api')
export const post = client('post', { url: 'posts/latest' })
post.fetch()
You can use the connect
Higher-Order Component to attach your resource state to your component and bind any action creators you want to use. Most of the time, you will be fetching something when the component mounts. If this is the only component that will use the resource, you can reset it when the component unmounts. Usually create
and update
action creators will be bound to the button or submit handlers on your form.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { post } from './resources'
export class MyComponent extends Component {
async componentDidMount() {
try {
const res = await this.props.fetchPost()
console.log(res)
} catch (error) {
alert('Something bad happened!')
}
}
componentWillUnmount = () => this.props.resetPost()
render() {
const { initialized, error, title, body, fetchPost } = this.props
if (!initialized) {
if (error) {
return <div className="error">{error.message}</div>
} else {
return <div className="loading">Loading...</div>
}
}
return (
<div>
<h1>{title}</h1>
<div className="body">
{body}
</div>
<div className="error">
{error.message}
</div>
<button type="button" onClick={fetchPost}>Refresh</button>
</div>
)
}
}
export function mapProps (state) {
const { ready, error, payload } = post(state)
const { data: { title, body } = {} } = payload
return { ready, error, title, body }
}
const actions = {
fetchPost: () => post.fetch({ id: 'latest' }),
resetPost: post.reset,
}
export default connect(mapProps, actions)(MyComponent)
The payload can be a massive object containing lots of information about the HTTP request and response, most of which you aren't going to need when you're rendering your component, so I suggest using the mapProps
call to simplify the payload to just the stuff you're going to need. Try to avoid using payload directly. Check out this blog post for further reading.
For details on mapProps, read the react-redux connect() documentation.
Installation
npm install --save redux-supermodel redux-promise-middleware
You will need to add the redux-promise-middleware
middleware and the redux-supermodel
reducer to your Redux Store.
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'
import promiseMiddleware from 'redux-promise-middleware'
import { reducer as resource } from 'redux-supermodel'
const rootReducer = combineReducers({ resource })
export default compose(applyMiddleware(promiseMiddleware()))(createStore)(rootReducer)