回答編集履歴
3
追記
test
CHANGED
@@ -155,10 +155,143 @@
|
|
155
155
|
```
|
156
156
|
|
157
157
|
|
158
|
+
|
159
|
+
|
158
|
-
###
|
160
|
+
### 追記
|
161
|
+
|
159
|
-
|
162
|
+
上記の回答コードに対する修正を追記しておきます。
|
163
|
+
|
164
|
+
- fetchState の取り得る値の型エイリアス `RequestState` を追加
|
165
|
+
- Action の typeプロパティが取り得る値の型エイリアス `ActionType` を追加
|
166
|
+
- Usersコンポーネントで fetchState の値ごとに表示内容を振り分け
|
167
|
+
|
168
|
+
### src/constants.ts
|
169
|
+
|
170
|
+
以下に差し替え
|
171
|
+
|
172
|
+
```typescript
|
160
|
-
|
173
|
+
const REQUEST_STATE = ['INITIAL', 'LOADING', 'OK', 'NG' ] as const;
|
174
|
+
|
161
|
-
|
175
|
+
type RequestState = typeof REQUEST_STATE[number];
|
176
|
+
|
162
|
-
|
177
|
+
export type { RequestState };
|
178
|
+
|
179
|
+
|
163
|
-
|
180
|
+
```
|
181
|
+
|
164
|
-
|
182
|
+
### src/reducers/users.ts
|
183
|
+
|
184
|
+
```diff
|
185
|
+
-import { REQUEST_STATE } from "../constants";
|
186
|
+
+import { RequestState } from "../constants";
|
187
|
+
import { User } from "../models";
|
188
|
+
|
189
|
+
interface State {
|
190
|
+
- fetchState: number
|
191
|
+
+ fetchState: RequestState
|
192
|
+
usersList: User[]
|
193
|
+
error: Error | null
|
194
|
+
}
|
195
|
+
|
196
|
+
+const actionTypes = ["FETCHING", "FETCH_SUCCESS", "FETCH_FAILURE"] as const;
|
197
|
+
+type ActionType = typeof actionTypes[number];
|
198
|
+
+
|
199
|
+
interface Action {
|
200
|
+
- type: string
|
201
|
+
+ type: ActionType
|
202
|
+
payload?: {
|
203
|
+
users: User[]
|
204
|
+
}
|
205
|
+
```
|
206
|
+
|
207
|
+
```diff
|
208
|
+
export const initialState: State = {
|
209
|
+
- fetchState: REQUEST_STATE.INITIAL,
|
210
|
+
+ fetchState: "INITIAL",
|
211
|
+
usersList: [],
|
212
|
+
error: null,
|
213
|
+
};
|
214
|
+
```
|
215
|
+
|
216
|
+
```diff
|
217
|
+
case "FETCHING":
|
218
|
+
return {
|
219
|
+
...state,
|
220
|
+
- fetchState: REQUEST_STATE.LOADING,
|
221
|
+
+ fetchState: "LOADING",
|
222
|
+
error: null,
|
223
|
+
};
|
224
|
+
case "FETCH_SUCCESS":
|
225
|
+
return {
|
226
|
+
...state,
|
227
|
+
- fetchState: REQUEST_STATE.OK,
|
228
|
+
+ fetchState: "OK",
|
229
|
+
usersList: action.payload?.users || [],
|
230
|
+
};
|
231
|
+
case "FETCH_FAILURE":
|
232
|
+
return {
|
233
|
+
...state,
|
234
|
+
- fetchState: REQUEST_STATE.NG,
|
235
|
+
- error: action.error || null
|
236
|
+
+ fetchState: "NG",
|
237
|
+
+ error: action.error || new Error("unknown error")
|
238
|
+
};
|
239
|
+
default:
|
240
|
+
return state;
|
241
|
+
```
|
242
|
+
|
243
|
+
### src/components/Users_index.tsx
|
244
|
+
|
245
|
+
```diff
|
246
|
+
export const Users = () => {
|
247
|
+
|
248
|
+
- const [state, dispatch] = useReducer(usersReducer, initialState);
|
249
|
+
+ const [{ fetchState, usersList, error }, dispatch] = useReducer(usersReducer, initialState);
|
250
|
+
|
251
|
+
useEffect(() => {
|
252
|
+
dispatch({ type: "FETCHING" });
|
253
|
+
|
254
|
+
```
|
255
|
+
|
256
|
+
```diff
|
257
|
+
users: data.users
|
258
|
+
}
|
259
|
+
})
|
260
|
+
- ).catch(e =>
|
261
|
+
- dispatch({
|
262
|
+
- type: "FETCH_ERROR",
|
263
|
+
- error: e
|
264
|
+
- })
|
265
|
+
- )
|
266
|
+
- }, [])
|
267
|
+
+ )
|
268
|
+
+ .catch(e =>
|
269
|
+
+ dispatch({
|
270
|
+
+ type: "FETCH_FAILURE",
|
271
|
+
+ error: e
|
272
|
+
+ })
|
273
|
+
+ )
|
274
|
+
+ }, []);
|
275
|
+
+
|
276
|
+
+ if (fetchState === "LOADING") {
|
277
|
+
+ return <div>データ取得中...</div>;
|
278
|
+
+ }
|
279
|
+
+
|
280
|
+
return (
|
281
|
+
<>
|
282
|
+
<p>ユーザー一覧ページです</p>
|
283
|
+
- {
|
284
|
+
- state.usersList.map(user =>
|
285
|
+
- <div key={user.id}>
|
286
|
+
- {user.name}
|
287
|
+
- </div>
|
288
|
+
- )
|
289
|
+
+ {error
|
290
|
+
+ ? <div>エラーが発生しました。原因: {error.message} </div>
|
291
|
+
+ : usersList.map(({ id, name }) => <div key={id}>{name}</div>)
|
292
|
+
}
|
293
|
+
</>
|
294
|
+
)
|
295
|
+
```
|
296
|
+
|
297
|
+
|
2
コード修正
test
CHANGED
@@ -86,7 +86,7 @@
|
|
86
86
|
import { usersIndex } from "../urls";
|
87
87
|
import { User } from "../models";
|
88
88
|
|
89
|
-
|
89
|
+
interface GetUsersResponse {
|
90
90
|
users: User[];
|
91
91
|
}
|
92
92
|
|
1
誤字修正
test
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
email: string
|
10
10
|
}
|
11
11
|
```
|
12
|
-
上記は質問にあるAPIレスポンスに含まれる1個のユーザーのプロパティを端折ったものです。実際はレスポンスに含まれる全プロパティに合わせて `interface User` を記述します。この `User` はAP、リデューサー、Reactコンポーネントのいずれからもimportされ得るものです。
|
12
|
+
上記は質問にあるAPIレスポンスに含まれる1個のユーザーのプロパティを端折ったものです。実際はレスポンスに含まれる全プロパティに合わせて `interface User` を記述します。この `User` はAPI、リデューサー、Reactコンポーネントのいずれからもimportされ得るものです。
|
13
13
|
|
14
14
|
- 補足:バックエンドがRailsのようなので上記のファイル名をとりあえず `models.ts`にしておくことについて違和感はそれほど無いと思いますが、人によっては一言モノ申したくなるかもしれません。
|
15
15
|
|