-
Notifications
You must be signed in to change notification settings - Fork 142
/
Copy paththunks.patch
133 lines (125 loc) · 4.21 KB
/
thunks.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
diff --git a/src/components.js b/src/components.js
index 11e6f74..d4480fd 100644
--- a/src/components.js
+++ b/src/components.js
@@ -13,4 +13,5 @@ const NotFound = () => <h3>404</h3>
export { Home, ConnectedUser as User, NotFound }
+export { default as Swapi } from './swapi';
diff --git a/src/configureStore.js b/src/configureStore.js
index aef2ce8..c32bacb 100644
--- a/src/configureStore.js
+++ b/src/configureStore.js
@@ -1,17 +1,14 @@
import { applyMiddleware, combineReducers, compose, createStore } from 'redux'
import { connectRoutes } from 'redux-first-router'
+import routesMap from './routesMap'
import page from './pageReducer'
-
-const routesMap = {
- HOME: '/',
- USER: '/user/:id'
-}
+import swapi from './swapiReducer'
export default function configureStore(preloadedState) {
const { reducer, middleware, enhancer } = connectRoutes(routesMap)
- const rootReducer = combineReducers({ page, location: reducer })
+ const rootReducer = combineReducers({ page, swapi, location: reducer })
const middlewares = applyMiddleware(middleware)
const enhancers = compose(enhancer, middlewares)
diff --git a/src/pageReducer.js b/src/pageReducer.js
index 582f04d..262f9d6 100644
--- a/src/pageReducer.js
+++ b/src/pageReducer.js
@@ -3,6 +3,7 @@ import { NOT_FOUND } from 'redux-first-router'
const components = {
HOME: 'Home',
USER: 'User',
+ SWAPI: 'Swapi',
[NOT_FOUND]: 'NotFound'
}
diff --git a/src/routesMap.js b/src/routesMap.js
new file mode 100644
index 0000000..0800aea
--- /dev/null
+++ b/src/routesMap.js
@@ -0,0 +1,39 @@
+export default {
+ HOME: '/',
+ USER: '/user/:id',
+ SWAPI: {
+ path: '/swapi/:type/:id',
+ thunk: async (dispatch, getState) => {
+ // Will only match this route if type and id are set. For optional segments, use ".../:type?/:id?".
+ const { type, id } = getState().location.payload
+ const url = `https://swapi.co/api/${type}/${id}`
+
+ try {
+ const response = await fetch(url)
+ if (response.ok) {
+ const data = await response.json()
+ // Note that only one action is required, since fetching is triggered by the router.
+ dispatch({ type: 'SWAPI_FETCHED', payload: { data } })
+ return
+ }
+ } catch (_) {}
+ // Something went wrong, update the response data with the API's usage overview, without changing route.
+ dispatch({ type: 'SWAPI_HELP' })
+ }
+ },
+ // Below is a _pathless_ route! Despite being defined in routesMap, it's not connected to the route,
+ // but is supported in order to have a uniform thunk interface even when routes are not involved.
+ // Defining all "setup" actions this way helps structure the app and reduce Redux boilerplate.
+ SWAPI_HELP: {
+ thunk: async (dispatch, getState) => {
+ const action = { type: 'SWAPI_FETCHED', payload: { hasError: true } };
+ try {
+ const response = await fetch('https://swapi.co/api/')
+ action.payload.data = response.ok ? await response.json() : `Status: ${response.status}`
+ } catch (error) {
+ action.payload.data = `Error: ${error.message}`
+ }
+ dispatch(action)
+ }
+ }
+}
diff --git a/src/swapi.js b/src/swapi.js
new file mode 100644
index 0000000..83394c1
--- /dev/null
+++ b/src/swapi.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import { connect } from 'react-redux'
+
+const Swapi = ({ data, hasError, query }) => {
+ const title = !data ? `SWAPI: Loading ${query}...` : `SWAPI: ${query}`;
+ const hint = <small><em>(Hint: try people/1/ or planets/3/ or starships/9/)</em></small>
+ return (
+ <div>
+ <h3>{title}</h3>
+ {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
+ {hasError && hint}
+ </div>
+ )
+}
+
+const mapStateToProps = ({ location: { payload }, swapi: { data, hasError }}) => ({
+ data,
+ hasError,
+ query: `${payload.type}/${payload.id}`
+})
+
+export default connect(mapStateToProps)(Swapi)
diff --git a/src/swapiReducer.js b/src/swapiReducer.js
new file mode 100644
index 0000000..302455b
--- /dev/null
+++ b/src/swapiReducer.js
@@ -0,0 +1,7 @@
+export default (state = {}, action = {}) => {
+ if (action.type !== 'SWAPI_FETCHED') {
+ return state
+ }
+ const { data, hasError = false } = action.payload
+ return { data, hasError }
+}