UNPKG

3.85 kBMarkdownView Raw
1# Request Layer
2
3This is the request layer used here at One Day Only to handle request tracking between clients and server, as well as optimistic updates.
4
5## The general idea:
6
7We do not directly make api requests from our clients but rather we pass actions via a socket server, which will later respond with the data we need. This is great, but due to its very
8asynchronous nature, its hard to keep track of requests. What this layer does is wrap each action with some meta information including a `requestId` and a `requestName` and then
9stores each request in state along with its current status (`pending`, `complete`, `failed`).
10
11## How to use
12
13We need to target 4 areas of the system:
14
15+ Redux middleware - to intercept wrapped actions. sure we could make thunk actions, but its easier to understand whats going on when the action system is sync.
16+ Action creators - to wrap an action with the required meta data.
17+ Reducers - to handle state mutations and provide optimistic updates.
18+ Socket Server - to interpret the clientAction and properly format a responseAction.
19
20```javascript
21/* Middleware */
22import { requestMiddleware } from 'request-layer'
23import io from 'socket.io-client'
24
25const socket = io("http:// ...")
26const store = createStore(
27 applyMiddleware(requestMiddleware(socket))
28)
29```
30
31```javascript
32/* Action Creators */
33import { serverAction } from 'request-layer'
34
35export const someAction = (payload: Object, getRequestId: Function) => serverAction({
36 type: String,
37 payload,
38 meta: {
39 optimistic: Boolean,
40 requestName: String
41 }
42}, getRequestId)
43```
44
45The reducer provided is actually a _reducer enhancer_ meaning it will wrap your reducers and quietly change state. The reducerEnhancer will also enhance your store with a
46redux-optimist reducer
47```javascript
48/* Reducer */
49import { reducerEnhancer } from 'request-layer'
50
51const roodReducer = combineReducers({ ... })
52export default reducerEnhancer(roodReducer)
53```
54
55```javascript
56/* Socket Server */
57import { serverMiddleware } from 'request-layer'
58
59type Action = {
60 type: string,
61 payload: ?any,
62 meta: ?Object
63}
64
65type HandlerParams = {
66 action: ServerAction,
67 resolve: (res: ResponseAction) => RequestAction,
68 reject: (e: any) => RequestAction,
69 socket: Object
70}
71
72const actionHandler = (params: HandlerParams) => {}
73
74serverMiddleware(actionHandler, socket)
75```
76
77Additionally, request-layer provides a react `HOC` that passes a collection of tools into a components props. These tools can be used to get the status of requests and handle
78complete/failed requests.
79```javascript
80import React, { Component } from 'react'
81import { requestTools, serverAction } from 'request-layer'
82import { connect } from 'react-redux'
83import _ from 'lodash'
84
85const enhance = _.flowRight(
86 connect(state => ({requests: state.requests})),
87 requestTools
88)
89export default
90enhance(class App extends Component {
91
92 addItem = item => {
93 store.dispatch(serverAction({
94 type: "ADD_ITEMS",
95 payload: item,
96 meta: {
97 requestName: "items.add"
98 }
99 }, id => {
100 this.props.trace(id)
101 .then(() => {
102 // request is complete
103 })
104 .catch(e => {
105 // request failed with error: e
106 })
107 }))
108 }
109
110 render() {
111 return this.props.pending("items.fetch",
112 <Loader></Loader>,
113
114 <div>
115 <Button disabled={this.props.pending("items.add")} onClick={this.addItem({name: "new item"})}>Add Item</Button>
116
117 {this.props.failed("items.add", "failed to add item")}
118 {this.props.complete("items.add", "successfully added item")}
119 </div>
120 )
121 }
122})
123```
\No newline at end of file