Skip to content

Commit 904569c

Browse files
authored
Change AppThunk definition for better typing (#185)
Reference: reduxjs/rtk-github-issues-example#1
1 parent e1c445b commit 904569c

File tree

1 file changed

+23
-27
lines changed

1 file changed

+23
-27
lines changed

docs/tutorials/advanced-tutorial.md

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ Since the thunk middleware is already set up, we don't have to do any work there
514514

515515
Before we go any further, let's add a type declaration we can reuse instead.
516516

517-
> - [Add AppThunk type](https://github.com/markerikson/rsk-github-issues-example/commit/9ba637353d40667dc13eb650d132bee0445eff5d)
517+
> - [Add AppThunk type](https://github.com/markerikson/rsk-github-issues-example/commit/2ac93bb089705847a8ce349864d885a5039eff4b)
518518
519519
**app/store.ts**
520520

@@ -528,27 +528,23 @@ Before we go any further, let's add a type declaration we can reuse instead.
528528

529529
export type AppDispatch = typeof store.dispatch
530530

531-
+export type AppThunk = ActionCreator<
532-
+ ThunkAction<void, RootState, null, Action<string>>
533-
+>
531+
+export type AppThunk = ThunkAction<void, RootState, null, Action<string>
534532
```
535533

536-
The `AppThunk` type declares that:
534+
The `AppThunk` type declares that the "action" that we're using is specifically a thunk function. The thunk is customized with some additional type parameters:
537535

538-
- This function is an "action creator" (which takes some arguments, and returns a specific action type)
539-
- The "action" that we're returning is specifically a thunk function. The thunk is customized with some additional type parameters:
540-
1. Return value: the thunk doesn't return anything
541-
2. State type for `getState`: returns our `RootState` type
542-
3. "Extra argument": the thunk middleware can be customized to pass in an extra value, but we aren't doing that in this app
543-
4. Action types accepted by `dispatch`: any action whose `type` is a string.
536+
1. Return value: the thunk doesn't return anything
537+
2. State type for `getState`: returns our `RootState` type
538+
3. "Extra argument": the thunk middleware can be customized to pass in an extra value, but we aren't doing that in this app
539+
4. Action types accepted by `dispatch`: any action whose `type` is a string.
544540

545541
There are many cases where you would want different type settings here, but these are probably the most common settings. This way, we can avoid repeating that same type declaration every time we write a thunk.
546542

547543
#### Adding the Repo Details Slice
548544

549545
Now that we have that type, we can write a slice of state for fetching details on a repo.
550546

551-
> - [Add a slice for storing repo details](https://github.com/markerikson/rsk-github-issues-example/commit/f4d5a1840028b645c600eecc3f8297e5cc6a6b93)
547+
> - [Add a slice for storing repo details](https://github.com/markerikson/rsk-github-issues-example/commit/da9291bf428a96c3f2e8862f42e3be08461d514c)
552548
553549
**features/repoSearch/repoDetailsSlice.ts**
554550

@@ -591,10 +587,10 @@ export const {
591587

592588
export default repoDetails.reducer
593589

594-
export const fetchIssuesCount: AppThunk = (
590+
export const fetchIssuesCount = (
595591
org: string,
596592
repo: string
597-
) => async dispatch => {
593+
): AppThunk => async dispatch => {
598594
try {
599595
const repoDetails = await getRepoDetails(org, repo)
600596
dispatch(getRepoDetailsSuccess(repoDetails))
@@ -609,7 +605,7 @@ The first part of this should look straightforward. We declare our slice state s
609605
Down at the bottom, we have our first data fetching thunk. The important things to notice here are:
610606

611607
- **The thunk is defined separately from the slice**, since RSK currently has no special syntax for defining thunks as part of a slice.
612-
- **We declare the thunk action creator as an arrow function, and use the `AppThunk` type we just created.** You can use either arrow functions or the `function` keyword to write thunk functions, but because we defined the `AppThunk` type as "an action creator that returns a thunk function", we need to apply that type to the action creator function. TS won't currently allow us to declare a type for a function declared with the `function` keyword, so we use an arrow function. (If we changed `AppThunk` to be just the "thunk function" part, we could write `function fetchIssuesCount() : AppThunk` as the return value instead.)
608+
- **We declare the thunk action creator as an arrow function, and use the `AppThunk` type we just created.** You can use either arrow functions or the `function` keyword to write thunk functions and thunk action creators, so we could also have written this as `function fetchIssueCount() : AppThunk` instead.
613609
- **We use the `async/await` syntax for the thunk function itself.** Again, this isn't required, but `async/await` usually results in simpler code than nested Promise `.then()` chains.
614610
- **Inside the thunk, we dispatch the plain action creators that were generated by the `createSlice` call**.
615611

@@ -619,7 +615,7 @@ While not shown, we also add the slice reducer to our root reducer.
619615

620616
Now that the repo details slice exists, we can use it in the `<IssuesListPage>` component.
621617

622-
> - [Update IssuesListPage to fetch repo details via Redux](https://github.com/markerikson/rsk-github-issues-example/commit/798a9bcf38eef1a1e5857057352c7b9e0a20cbcd)
618+
> - [Update IssuesListPage to fetch repo details via Redux](https://github.com/markerikson/rsk-github-issues-example/commit/964134a00bc1a54ba8758ca274049c9174e88f9a)
623619
624620
**features/issuesList/IssuesListPage.tsx**
625621

@@ -698,7 +694,7 @@ Inside our `useEffect`, we drop the `fetchIssueCount` function, and dispatch `fe
698694

699695
Next up, we need to replace the logic for fetching a list of open issues.
700696

701-
> - [Add a slice for tracking issues state](https://github.com/markerikson/rsk-github-issues-example/commit/43679930bfec13a6105337b5d8733980e200ceb5)
697+
> - [Add a slice for tracking issues state](https://github.com/markerikson/rsk-github-issues-example/commit/b2e5919651a5076e3857da96321bc979a8ae54b9)
702698
703699
**features/issuesList/issuesSlice.ts**
704700

@@ -777,11 +773,11 @@ export const {
777773

778774
export default issues.reducer
779775

780-
export const fetchIssues: AppThunk = (
776+
export const fetchIssues = (
781777
org: string,
782778
repo: string,
783779
page?: number
784-
) => async dispatch => {
780+
): AppThunk => async dispatch => {
785781
try {
786782
dispatch(getIssuesStart())
787783
const issues = await getIssues(org, repo, page)
@@ -791,11 +787,11 @@ export const fetchIssues: AppThunk = (
791787
}
792788
}
793789

794-
export const fetchIssue: AppThunk = (
790+
export const fetchIssue = (
795791
org: string,
796792
repo: string,
797793
number: number
798-
) => async dispatch => {
794+
): AppThunk => async dispatch => {
799795
try {
800796
dispatch(getIssueStart())
801797
const issue = await getIssue(org, repo, number)
@@ -815,7 +811,7 @@ This slice is a bit longer, but it's the same basic approach as before: write th
815811

816812
Now we can finish converting the `<IssuesListPage>` component by swapping out the issues fetching logic.
817813

818-
> - [Update IssuesListPage to fetch issues data via Redux](https://github.com/markerikson/rsk-github-issues-example/commit/87ee62d93cff3b8686ee5a774a389e681423ab01)
814+
> - [Update IssuesListPage to fetch issues data via Redux](https://github.com/markerikson/rsk-github-issues-example/commit/8dbdc0726ccecf354a01351786196648c752c0a6)
819815
820816
Let's look at the changes.
821817

@@ -954,7 +950,7 @@ It's very similar to `<IssuesListPage>`. We store the current displayed `Issue`,
954950

955951
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>`.
956952

957-
> - [Update IssueDetailsPage to fetch issue data via Redux](https://github.com/markerikson/rsk-github-issues-example/commit/a57fe3f97ccb26ee2c481c2f71057a58c7fd4dad)
953+
> - [Update IssueDetailsPage to fetch issue data via Redux](https://github.com/markerikson/rsk-github-issues-example/commit/46bcddbe1078574fab649a13f61a6bf3d0f42839)
958954
959955
**features/issueDetails/IssueDetailsPage.tsx**
960956

@@ -1019,7 +1015,7 @@ Interestingly, there's actually a bit of a change in behavior here. The original
10191015

10201016
We have one more slice left to write - we need to fetch and store comments for the current issue.
10211017

1022-
> - [Add a slice for tracking comments data](https://github.com/markerikson/rsk-github-issues-example/commit/e765f09b44a75cb99b3e2866e3ff2806a533620f)
1018+
> - [Add a slice for tracking comments data](https://github.com/markerikson/rsk-github-issues-example/commit/46bcddbe1078574fab649a13f61a6bf3d0f42839)
10231019
10241020
**features/issueDetails/commentsSlice.ts**
10251021

@@ -1074,7 +1070,7 @@ export const {
10741070
} = comments.actions
10751071
export default comments.reducer
10761072

1077-
export const fetchComments: AppThunk = (issue: Issue) => async dispatch => {
1073+
export const fetchComments = (issue: Issue): AppThunk => async dispatch => {
10781074
try {
10791075
dispatch(getCommentsStart())
10801076
const comments = await getComments(issue.comments_url)
@@ -1091,7 +1087,7 @@ The slice should look pretty familiar at this point. Our main bit of state is a
10911087

10921088
The final step is to swap the comments fetching logic in `<IssueDetailsPage>`.
10931089

1094-
> - [Update IssueDetailsPage to fetch comments via Redux](https://github.com/markerikson/rsk-github-issues-example/commit/0977da34631088981f58f9d160573d83d4225238)
1090+
> - [Update IssueDetailsPage to fetch comments via Redux](https://github.com/markerikson/rsk-github-issues-example/commit/9d1246a4d89f21da1f0e5377f040bc766e1fc0fd)
10951091
10961092
**features/issueDetails/IssueDetailsPage.tsx**
10971093

@@ -1177,6 +1173,6 @@ Hopefully you now have a solid understand of how Redux Starter Kit looks in a re
11771173

11781174
Let's wrap this up with one more look at the complete source code and the running app:
11791175

1180-
<iframe src="https://codesandbox.io/embed/rsk-github-issues-example-03-final-m55m4?fontsize=14&module=%2Fsrc%2Ffeatures%2FissueDetails%2FcommentsSlice.ts&view=editor" title="rsk-github-issues-example-03-final" allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>
1176+
<iframe src="https://codesandbox.io/embed/rsk-github-issues-example-03-final-yz8ip?fontsize=14&module=%2Fsrc%2Ffeatures%2FissueDetails%2FcommentsSlice.ts&view=editor" title="rsk-github-issues-example-03-final" allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>
11811177

11821178
Now, go out there and build something!

0 commit comments

Comments
 (0)