You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -52,15 +57,15 @@ The codebase is already laid out in a "feature folder" structure, The main piece
52
57
53
58
Since this app doesn't yet use Redux at all, the first step is to install Redux Starter Kit and React-Redux. Since this is a TypeScript app, we'll also need to add `@types/react-redux` as well. Add those packages to the project via either Yarn or NPM.
54
59
55
-
> -[Add Redux Starter Kit and React-Redux packages](https://github.com/reduxjs/rsk-github-issues-example/commit/8f69804d8940ba40604949ca682a7ae968e8bc4f)
60
+
> -[Add Redux Starter Kit and React-Redux packages](https://github.com/reduxjs/rsk-github-issues-example/commit/83ae4753952060956a303a3aa983c8300facb974)
56
61
57
62
Next, we need to set up the usual pieces: a root reducer function, the Redux store, and the `<Provider>` to make that store available to our component tree.
58
63
59
64
In the process, we're going to set up "Hot Module Replacement" for our app. That way, whenever we make a change to the reducer logic or the component tree, Create-React-App will rebuild the app and swap the changed code into our running app, without having to completely refresh the page.
60
65
61
66
#### Creating the Root Reducer
62
67
63
-
> -[Add store and root reducer with reducer HMR](https://github.com/reduxjs/rsk-github-issues-example/commit/26054ea8be1a44cac75fd55f497ce20e264de2b0)
68
+
> -[Add store and root reducer with reducer HMR](https://github.com/reduxjs/rsk-github-issues-example/commit/e80552e59e7832208c964ba1eefcfad24d41f317)
64
69
65
70
First, we'll create the root reducer function. We don't have any slices yet, so it will just return an empty object.
66
71
@@ -113,7 +118,7 @@ The `require('./rootReducer').default` looks a bit odd. That's because we're mix
113
118
114
119
Now that the store has been created, we can add it to the React component tree.
115
120
116
-
> -[Render Redux Provider with app HMR](https://github.com/reduxjs/rsk-github-issues-example/commit/49cf5caebd427e7bb6b7ab07098c3bbb12134faf)
121
+
> -[Render Redux Provider with app HMR](https://github.com/reduxjs/rsk-github-issues-example/commit/647d4858da979d330a043fc629bd08a6bf21c23d)
117
122
118
123
As with the root reducer, we can hot-reload the React component tree whenever a component file changes. The best way is to write a function that imports the `<App>` component and renders it, call that once on startup to show the React component tree as usual, and then reuse that function any time a component is changed.
119
124
@@ -170,7 +175,7 @@ The first step is to look at the data that is currently being kept in `<App>`, a
170
175
171
176
Let's look at the source for the whole slice, and then break down what it's doing:
172
177
173
-
> -[Add initial state slice for UI display](https://github.com/reduxjs/rsk-github-issues-example/commit/ec809346d5afe8f96bb56e487c2e41d274d80c69)
178
+
> -[Add initial state slice for UI display](https://github.com/reduxjs/rsk-github-issues-example/commit/daf082e161eaede49b48d92fdf8cb921ed80ea9b)
174
179
175
180
**features/issuesDisplay/issuesDisplaySlice.ts**
176
181
@@ -288,7 +293,7 @@ import { combineReducers } from 'redux-starter-kit'
288
293
289
294
Now that the issues display slice is hooked up to the store, we can update `<App>` to use that instead of its internal component state.
290
295
291
-
> -[Convert main issues display control to Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/07bea70da4439c4c38b9b8d4eb0f10c67e6feee2)
296
+
> -[Convert main issues display control to Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/e518e1936d5bdaeeb3cf3d6d0ac8496d12c1109b)
292
297
293
298
We need to make three groups of changes to the `App` component:
294
299
@@ -375,7 +380,12 @@ Unlike typical `connect` + `mapDispatch` usage, here we call `dispatch()` direct
If you're thinking "hey, this looks and behaves exactly like the previous example"... then that's great! That means we've correctly converted the first bit of logic to Redux so far. If you want to confirm that there's Redux logic running, try clicking the "Open in New Window" button and inspect the store in the Redux DevTools Extension.
381
391
@@ -496,6 +506,9 @@ For further explanations, see [these articles explaining thunks in the `redux-th
496
506
497
507
There are many other kinds of Redux middleware that add async capabilities. The most popular are [`redux-saga`](https://redux-saga.js.org/), which uses generator functions, and [`redux-observable`](https://redux-observable.js.org/), which uses RxJS observables. For some comparisons, see the [Redux FAQ entry on "how do I choose an async middleware?"](https://redux.js.org/faq/actions#what-async-middleware-should-i-use-how-do-you-decide-between-thunks-sagas-observables-or-something-else).
498
508
509
+
However, while sagas and observables are useful, most apps do not need the power and capabilities they provide. So, **thunks are
510
+
the default recommended approach for writing async logic with Redux**.
511
+
499
512
#### Writing Thunks in Redux Starter Kit
500
513
501
514
Writing thunk functions requires that the `redux-thunk` middleware be added to the store as part of the setup process. Redux Starter Kit's `configureStore` function does automatically - [`thunk` is one of the default middleware](../api/getDefaultMiddleware.md).
@@ -514,7 +527,7 @@ Since the thunk middleware is already set up, we don't have to do any work there
514
527
515
528
Before we go any further, let's add a type declaration we can reuse instead.
@@ -544,7 +557,7 @@ There are many cases where you would want different type settings here, but thes
544
557
545
558
Now that we have that type, we can write a slice of state for fetching details on a repo.
546
559
547
-
> -[Add a slice for storing repo details](https://github.com/reduxjs/rsk-github-issues-example/commit/da9291bf428a96c3f2e8862f42e3be08461d514c)
560
+
> -[Add a slice for storing repo details](https://github.com/reduxjs/rsk-github-issues-example/commit/061f8230b4e8f77664b023c17786a22e57f8ed3a)
548
561
549
562
**features/repoSearch/repoDetailsSlice.ts**
550
563
@@ -611,11 +624,43 @@ Down at the bottom, we have our first data fetching thunk. The important things
611
624
612
625
While not shown, we also add the slice reducer to our root reducer.
613
626
627
+
#### Async Error Handling Logic in Thunks
628
+
629
+
There is one potential flaw with the `fetchIssuesCount()` thunk as written. The `try/catch` block will currently catch any errors thrown
630
+
by `getRepoDetails()` (such as an actual failed AJAX call), but it will also catch any errors that occur inside the dispatch of `getRepoDetailsSuccess()`. In both cases, it will end up dispatch `getRepoDetailsFailed()`. This may not be the desired way to handle errors, as it might show a misleading reason for what the actual error was.
631
+
632
+
There are some possible ways to restructure the code to avoid this problem. First, the `await` could be switched to a standard promise chain, with separate callbacks passed in for the success and failure cases:
Or, the thunk could be rewritten to only dispatch if no errors were caught:
644
+
645
+
```ts
646
+
let repoDetails
647
+
try {
648
+
repoDetails=awaitgetRepoDetails(org, repo)
649
+
} catch (err) {
650
+
dispatch(getRepoDetailsFailed(err.toString()))
651
+
return
652
+
}
653
+
dispatch(getRepoDetailsSuccess(repoDetails))
654
+
}
655
+
```
656
+
657
+
For sake of simplicity, we'll stick with the logic as-is for the rest of the tutorial.
658
+
614
659
### Fetching Repo Details in the Issues List
615
660
616
661
Now that the repo details slice exists, we can use it in the `<IssuesListPage>` component.
617
662
618
-
> -[Update IssuesListPage to fetch repo details via Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/964134a00bc1a54ba8758ca274049c9174e88f9a)
663
+
> -[Update IssuesListPage to fetch repo details via Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/efe41bcebb835e714e197a66c93998d02efaeab2)
619
664
620
665
**features/issuesList/IssuesListPage.tsx**
621
666
@@ -694,7 +739,7 @@ Inside our `useEffect`, we drop the `fetchIssueCount` function, and dispatch `fe
694
739
695
740
Next up, we need to replace the logic for fetching a list of open issues.
696
741
697
-
> -[Add a slice for tracking issues state](https://github.com/reduxjs/rsk-github-issues-example/commit/b2e5919651a5076e3857da96321bc979a8ae54b9)
742
+
> -[Add a slice for tracking issues state](https://github.com/reduxjs/rsk-github-issues-example/commit/a358f4da20242a6213acdd71988f0f60d7a9a61e)
698
743
699
744
**features/issuesList/issuesSlice.ts**
700
745
@@ -811,7 +856,7 @@ This slice is a bit longer, but it's the same basic approach as before: write th
811
856
812
857
Now we can finish converting the `<IssuesListPage>` component by swapping out the issues fetching logic.
813
858
814
-
> -[Update IssuesListPage to fetch issues data via Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/8dbdc0726ccecf354a01351786196648c752c0a6)
859
+
> -[Update IssuesListPage to fetch issues data via Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/24a615f2ef5fbac793a4d0ea4163defd6ffd9222)
815
860
816
861
Let's look at the changes.
817
862
@@ -950,7 +995,7 @@ It's very similar to `<IssuesListPage>`. We store the current displayed `Issue`,
950
995
951
996
We conveniently already have the Redux logic for fetching a single issue - we wrote that already as part of `issuesSlice.ts`. So, we can immediately jump straight to using that here in `<IssueDetailsPage>`.
952
997
953
-
> -[Update IssueDetailsPage to fetch issue data via Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/46bcddbe1078574fab649a13f61a6bf3d0f42839)
998
+
> -[Update IssueDetailsPage to fetch issue data via Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/fdd589dafa1ebb6b67a58d2f6a45c2be2ca54708)
954
999
955
1000
**features/issueDetails/IssueDetailsPage.tsx**
956
1001
@@ -1015,7 +1060,7 @@ Interestingly, there's actually a bit of a change in behavior here. The original
1015
1060
1016
1061
We have one more slice left to write - we need to fetch and store comments for the current issue.
1017
1062
1018
-
> -[Add a slice for tracking comments data](https://github.com/reduxjs/rsk-github-issues-example/commit/46bcddbe1078574fab649a13f61a6bf3d0f42839)
1063
+
> -[Add a slice for tracking comments data](https://github.com/reduxjs/rsk-github-issues-example/commit/002d92e75f76fcc8dffe9a4ba61f7c03d4ce2222)
1019
1064
1020
1065
**features/issueDetails/commentsSlice.ts**
1021
1066
@@ -1087,7 +1132,7 @@ The slice should look pretty familiar at this point. Our main bit of state is a
1087
1132
1088
1133
The final step is to swap the comments fetching logic in `<IssueDetailsPage>`.
1089
1134
1090
-
> -[Update IssueDetailsPage to fetch comments via Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/9d1246a4d89f21da1f0e5377f040bc766e1fc0fd)
1135
+
> -[Update IssueDetailsPage to fetch comments via Redux](https://github.com/reduxjs/rsk-github-issues-example/commit/b9ec0600c98f875f0b331de335077f01a31e624d)
1091
1136
1092
1137
**features/issueDetails/IssueDetailsPage.tsx**
1093
1138
@@ -1173,6 +1218,11 @@ Hopefully you now have a solid understand of how Redux Starter Kit looks in a re
1173
1218
1174
1219
Let's wrap this up with one more look at the complete source code and the running app:
0 commit comments