@@ -5,7 +5,7 @@ import '@babel/polyfill';
5
5
6
6
import { useAsync , useAsyncAbortable , UseAsyncReturn } from 'react-async-hook' ;
7
7
8
- import { useState } from 'react' ;
8
+ import { ReactNode , useState } from 'react' ;
9
9
import useConstant from 'use-constant' ;
10
10
import AwesomeDebouncePromise from 'awesome-debounce-promise' ;
11
11
@@ -29,6 +29,23 @@ const fetchStarwarsHero = async (
29
29
return result . json ( ) ;
30
30
} ;
31
31
32
+ const searchStarwarsHero = async (
33
+ text : string ,
34
+ abortSignal ?: AbortSignal
35
+ ) : Promise < StarwarsHero [ ] > => {
36
+ const result = await fetch (
37
+ `https://swapi.co/api/people/?search=${ encodeURIComponent ( text ) } ` ,
38
+ {
39
+ signal : abortSignal ,
40
+ }
41
+ ) ;
42
+ if ( result . status !== 200 ) {
43
+ throw new Error ( 'bad status = ' + result . status ) ;
44
+ }
45
+ const json = await result . json ( ) ;
46
+ return json . results ;
47
+ } ;
48
+
32
49
const HeroContainer = ( { children } ) => (
33
50
< div
34
51
style = { {
@@ -47,6 +64,40 @@ const HeroContainer = ({ children }) => (
47
64
</ div >
48
65
) ;
49
66
67
+ const Example = ( {
68
+ title,
69
+ children,
70
+ } : {
71
+ title : string ;
72
+ children : ReactNode ;
73
+ } ) => (
74
+ < div
75
+ style = { {
76
+ margin : 20 ,
77
+ padding : 20 ,
78
+ border : 'solid' ,
79
+ } }
80
+ >
81
+ < h1
82
+ style = { {
83
+ marginBottom : 10 ,
84
+ paddingBottom : 10 ,
85
+ borderBottom : 'solid thin red' ,
86
+ } }
87
+ >
88
+ { title }
89
+ </ h1 >
90
+ < div
91
+ style = { {
92
+ marginBottom : 10 ,
93
+ paddingBottom : 10 ,
94
+ } }
95
+ >
96
+ { children }
97
+ </ div >
98
+ </ div >
99
+ ) ;
100
+
50
101
const StarwarsHeroRender = ( {
51
102
id,
52
103
asyncHero,
@@ -117,15 +168,7 @@ const StarwarsHeroLoader = ({
117
168
}
118
169
} ;
119
170
120
- const buttonStyle = {
121
- border : 'solid' ,
122
- cursor : 'pointer' ,
123
- borderRadius : 50 ,
124
- padding : 10 ,
125
- margin : 10 ,
126
- } ;
127
-
128
- const StarwarsExample = ( {
171
+ const StarwarsSliderExample = ( {
129
172
title,
130
173
exampleType,
131
174
} : {
@@ -135,23 +178,16 @@ const StarwarsExample = ({
135
178
const [ heroId , setHeroId ] = useState ( 1 ) ;
136
179
const next = ( ) => setHeroId ( heroId + 1 ) ;
137
180
const previous = ( ) => setHeroId ( heroId - 1 ) ;
181
+
182
+ const buttonStyle = {
183
+ border : 'solid' ,
184
+ cursor : 'pointer' ,
185
+ borderRadius : 50 ,
186
+ padding : 10 ,
187
+ margin : 10 ,
188
+ } ;
138
189
return (
139
- < div
140
- style = { {
141
- margin : 20 ,
142
- padding : 20 ,
143
- border : 'solid' ,
144
- } }
145
- >
146
- < h1
147
- style = { {
148
- marginBottom : 10 ,
149
- paddingBottom : 10 ,
150
- border : 'solid' ,
151
- } }
152
- >
153
- { title }
154
- </ h1 >
190
+ < Example title = { title } >
155
191
< div style = { { display : 'flex' } } >
156
192
< div style = { buttonStyle } onClick = { previous } >
157
193
Previous
@@ -172,7 +208,73 @@ const StarwarsExample = ({
172
208
< StarwarsHeroLoader id = { `${ heroId + 2 } ` } exampleType = { exampleType } />
173
209
</ HeroContainer >
174
210
</ div >
175
- </ div >
211
+ </ Example >
212
+ ) ;
213
+ } ;
214
+
215
+ const useSearchStarwarsHero = ( ) => {
216
+ // Handle the input text state
217
+ const [ inputText , setInputText ] = useState ( '' ) ;
218
+
219
+ // Debounce the original search async function
220
+ const debouncedSearchStarwarsHero = useConstant ( ( ) =>
221
+ AwesomeDebouncePromise ( searchStarwarsHero , 300 )
222
+ ) ;
223
+
224
+ const search = useAsyncAbortable (
225
+ async ( abortSignal , text ) => {
226
+ // If the input is empty, return nothing immediately (without the debouncing delay!)
227
+ if ( text . length === 0 ) {
228
+ return [ ] ;
229
+ }
230
+ // Else we use the debounced api
231
+ else {
232
+ return debouncedSearchStarwarsHero ( text , abortSignal ) ;
233
+ }
234
+ } ,
235
+ // Ensure a new request is made everytime the text changes (even if it's debounced)
236
+ [ inputText ]
237
+ ) ;
238
+
239
+ // Return everything needed for the hook consumer
240
+ return {
241
+ inputText,
242
+ setInputText,
243
+ search,
244
+ } ;
245
+ } ;
246
+
247
+ const SearchStarwarsHeroExample = ( ) => {
248
+ const { inputText, setInputText, search } = useSearchStarwarsHero ( ) ;
249
+ return (
250
+ < Example title = { 'Search starwars hero' } >
251
+ < input
252
+ value = { inputText }
253
+ onChange = { e => setInputText ( e . target . value ) }
254
+ placeholder = "Search starwars hero"
255
+ style = { {
256
+ marginTop : 20 ,
257
+ padding : 10 ,
258
+ border : 'solid thin' ,
259
+ borderRadius : 5 ,
260
+ width : 300 ,
261
+ } }
262
+ />
263
+ < div style = { { marginTop : 20 } } >
264
+ { search . loading && < div > ...</ div > }
265
+ { search . error && < div > Error: { search . error . message } </ div > }
266
+ { search . result && (
267
+ < div >
268
+ < div > Results: { search . result . length } </ div >
269
+ < ul >
270
+ { search . result . map ( hero => (
271
+ < li key = { hero . name } > { hero . name } </ li >
272
+ ) ) }
273
+ </ ul >
274
+ </ div >
275
+ ) }
276
+ </ div >
277
+ </ Example >
176
278
) ;
177
279
} ;
178
280
@@ -209,20 +311,21 @@ const App = () => (
209
311
</ h2 >
210
312
</ div >
211
313
212
- < StarwarsExample
213
- title = { 'Starwars hero example (basic)' }
314
+ < SearchStarwarsHeroExample />
315
+ < StarwarsSliderExample
316
+ title = { 'Starwars hero slider example (basic)' }
214
317
exampleType = "basic"
215
318
/>
216
- < StarwarsExample
217
- title = { 'Starwars hero example (debounced)' }
319
+ < StarwarsSliderExample
320
+ title = { 'Starwars hero slider example (debounced)' }
218
321
exampleType = "debounced"
219
322
/>
220
- < StarwarsExample
221
- title = { 'Starwars hero example (abortable)' }
323
+ < StarwarsSliderExample
324
+ title = { 'Starwars hero slider example (abortable)' }
222
325
exampleType = "abortable"
223
326
/>
224
- < StarwarsExample
225
- title = { 'Starwars hero example (merge)' }
327
+ < StarwarsSliderExample
328
+ title = { 'Starwars hero slider example (merge)' }
226
329
exampleType = "merge"
227
330
/>
228
331
</ div >
0 commit comments