回答編集履歴

3

追記

2022/10/10 19:04

投稿

退会済みユーザー
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
- **[IMO]** JSのコードをTSに移行するときは「全部いっぺんに正しい型付けをしよう」などとは思わず、`any` `as` を使いまくって「ファイル名の拡張子を `ts(x)` ファイルにしただけのほとんどJSなコード」にしてでもJSのときのとおりに動作するかを確認し、`any` や `as` を使ったところに `// TODO` コメントするなりして気長にやるのがコツです。
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

コード修正

2022/10/10 12:11

投稿

退会済みユーザー
test CHANGED
@@ -86,7 +86,7 @@
86
86
  import { usersIndex } from "../urls";
87
87
  import { User } from "../models";
88
88
 
89
- export interface GetUsersResponse {
89
+ interface GetUsersResponse {
90
90
  users: User[];
91
91
  }
92
92
 

1

誤字修正

2022/10/10 12:07

投稿

退会済みユーザー
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