redux-observable-sans
Version:
Use redux without any boilerplate
131 lines (89 loc) • 4.41 kB
Markdown
# redux-observable-sans-boilerplate
Redux is awesome. But, boilerplate is a code smell.
This library allows you to use redux with zero boilerplate:
* ZERO constant definitions
* ZERO action definitions
* ONLY 1 dispatch definition (per http API targeted)
(and similar for websockets).
Instead of boilerplate, you should **just be coding business logic**.
# install
```
npm i --save redux-observable-sans-boilerplate
```
# use
See `example` directory for a simple example of how to use library
## http (json)
```javascript
import { httpEpic, httpAction } from 'redux-observable-sans-boilerplate'
```
Setting up the middleware as per https://redux-observboilerplateable.js.org/docs/basics/SettingUpTheMiddleware.html
For our example we will be doing http request to two different backends.
```javascript
const optionsGH = {
url: 'https://api.github.com', // site that will be
id = 'GH', // optional value used to differentiate http action types
}
const optionsCustom = {
url: 'https://mysite.url',
// optionally use setHeaders to do authentication
setHeaders: (state$, deps) => ({ Authorization: `Bearer ${action$.value.me.token}` }),
}
const rootEpic = combineReducers(httpEpic(optionsGH),httpEpic(optionsCustom))
```
The action creators have the signature `httpAction(dispatch, id)(type, url, method = 'GET', settings = {})(body)` where:
* `dispatch` as provided by `reaca-redux`
* `id` is optional and should be the same value as used to create the `httpEpic`
* `type` is the front part of the type of action you will be dispatching, eg, `'GET_USERS'`:
* `httpEpic` will turn the dispacted action into `GET_USERS_REQUEST`, `GET_USERS_SUCCESS` and `GET_USERS_FAILURE` type actions
* the `_FAILURE` will be followed by either a `HTTP_ERROR` or `HTTP_UNAUTHORIZED` type action
* if `id` is defined, eg, `GH`, then the following types would be changed to `HTTP_GH_ERROR` or `HTTP_GH_UNAUTHORIZED`
* a `GET_USERS_CANCELLED` type action can be used to cancel the ongoing request
* `url` is the latter part of the url path; it will be appended to front piece defined in the corresponding `httpEpic`
* `method` can be `GET`, `POST`, `PATCH`, etc.
* `settings` is the standard options that can be passed to `fetch`
* `body` is optional; it is the JSON object, eg, from you form
To use you all you need to do is use `connect(mapStateToProps, mapDispatchToProps)` from `react-redux` where
```javascript
const mapDispatchToProps = dispatch => ({
http: httpAction(dispatch, id), // same id as in corresponding httpEpic
})
```
which you can call from anywhere in you app, eg,
```javascript
const mapDispatchToProps = (dispatch, ownProps) => ({
onSubmit: ownProps.http('GET_USERS', 'users'),
})
```
where your data is then passed into `onSubmit(data)`.
## websocket: command and query (and "event")
I created a `command` and `query` for websockets for convention sake, but it works perfectly fine if you just use `command`.
```javascript
import { socketEpics, commandAction, toAction } from 'redux-observable-sans-boilerplate'
```
Setting up the middleware as per https://redux-observboilerplateable.js.org/docs/basics/SettingUpTheMiddleware.html
You need to have defined a `socket` object
```javascript
const auth = (state$, deps) =>
({ token: state$.value.me.token, userId: state$.value.me.userId })
const rootEpic = combineReducers(...socketEpics(socket))
```
Note: The `auth` function is completely optional.
To use you all you need to do is use `connect(mapStateToProps, mapDispatchToProps)` from `react-redux` where
```javascript
const mapDispatchToProps = dispatch => ({
command: commandAction(dispatch),
})
```
which you can call from anywhere in you app, eg,
```javascript
const mapDispatchToProps = (dispatch, ownProps) => ({
onSubmit: ownProps.command('updateUser'),
})
```
where your data is then passed into `onSubmit(data)`.
Bonus: to handle events from backend you can use
```javascript
socket.on(`event`, toAction(dispatch))
```
it expects the data object received to include a `type` which will then be converted constant-case, and then dispatched as an action.
If a `CommandRejected` or `QueryRejected` type is received a further action of `_FAILURE` type will also be emitted (based on the constant-case of the `payload.type` -- idealy that would be the type of the corresponding command or query).