Skip to content

Commit e4864ca

Browse files
committed
Part 6 screenshots and sandbox
1 parent 945f98a commit e4864ca

File tree

8 files changed

+31
-10
lines changed

8 files changed

+31
-10
lines changed

docs/tutorials/essentials/part-6-performance-normalization.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,28 @@ From there, we know that the list of notifications is in our Redux store state,
424424
425425
### Adding the Notifications List
426426
427-
Now that we've got the `notificationsSlice` created, we can add a `<NotificationsList>` component. It needs to read the list of notifications from the store and format them, including showing how recent each notification was, and who sent it. We already have the `<PostAuthor>` and `<TimeAgo>` components that can do that formatting, so we can reuse them here.
427+
Now that we've got the `notificationsSlice` created, we can add a `<NotificationsList>` component. It needs to read the list of notifications from the store and format them, including showing how recent each notification was, and who sent it. We already have the `<PostAuthor>` and `<TimeAgo>` components that can do that formatting, so we can reuse them here. That said, `<PostAuthor>` includes a "by " prefix which doesn't make sense here - we'll modify it to add a `showPrefix` prop that defaults to `true`, and specifically _not_ show prefixes here.
428+
429+
```tsx title="features/posts/PostAuthor.tsx"
430+
interface PostAuthorProps {
431+
userId: string
432+
// highlight-next-line
433+
showPrefix?: boolean
434+
}
435+
436+
// highlight-next-line
437+
export const PostAuthor = ({ userId, showPrefix = true }: PostAuthorProps) => {
438+
const author = useAppSelector(state => selectUserById(state, userId))
439+
440+
return (
441+
<span>
442+
// highlight-next-line
443+
{showPrefix ? 'by ' : null}
444+
{author?.name ?? 'Unknown author'}
445+
</span>
446+
)
447+
}
448+
```
428449
429450
```tsx title="features/notifications/NotificationsList.tsx"
430451
import { useAppSelector } from '@/app/hooks'
@@ -443,7 +464,7 @@ export const NotificationsList = () => {
443464
<div key={notification.id} className="notification">
444465
<div>
445466
<b>
446-
<PostAuthor userId={notification.user} />
467+
<PostAuthor userId={notification.user} showPrefix={false} />
447468
</b>{' '}
448469
{notification.message}
449470
</div>
@@ -673,7 +694,7 @@ export const NotificationsList = () => {
673694
<div key={notification.id} className={notificationClassname}>
674695
<div>
675696
<b>
676-
<PostAuthor userId={notification.user} />
697+
<PostAuthor userId={notification.user} showPrefix={false} />
677698
</b>{' '}
678699
{notification.message}
679700
</div>
@@ -703,8 +724,6 @@ This does actually show that **it's possible to dispatch an action and not have
703724
704725
Here's how the notifications tab looks now that we've got the "new/read" behavior working:
705726
706-
**[TODO] Update screenshot**
707-
708727
![New notifications](/img/tutorials/essentials/notifications-new.png)
709728
710729
#### Showing Unread Notifications
@@ -894,7 +913,7 @@ selectPostsByUser(state3, 'user2')
894913
895914
Now that we've memoized `selectPostsByUser`, we can try repeating the React profiler with `<UserPage>` open while fetching notifications. This time we should see that `<UserPage>` doesn't re-render:
896915
897-
**[TODO] React perf profiler screenshot**
916+
![React DevTools Profiler optimized render capture - <UserPage>](/img/tutorials/essentials/userpage-optimized.png)
898917
899918
### Balancing Selector Usage
900919
@@ -1128,15 +1147,15 @@ First, we import `createEntityAdapter`, and call it to create our `postsAdapter`
11281147
11291148
`getInitialState()` returns an empty `{ids: [], entities: {}}` normalized state object. Our `postsSlice` needs to keep the `status` and `error` fields for loading state too, so we pass those in to `getInitialState()`.
11301149
1131-
Now that our posts are being kept as a lookup table in `state.entities`, we can change our `reactionAdded` and `postUpdated` reducers to directly look up the right posts by their IDs via `state.entities[postId], instead of having to loop over the old `posts` array.
1150+
Now that our posts are being kept as a lookup table in `state.entities`, we can change our `reactionAdded` and `postUpdated` reducers to directly look up the right posts by their IDs via `state.entities[postId]`, instead of having to loop over the old `posts` array.
11321151
11331152
When we receive the `fetchPosts.fulfilled` action, we can use the `postsAdapter.setAll` function as the to add all of the incoming posts to the state, by passing in the draft `state` and the array of posts in `action.payload`. This is an example of using the adapter methods as "mutating" helper functions inside of a `createSlice` reducer.
11341153
11351154
When we receive the `addNewPost.fulfilled` action, we know we need to add that one new post object to our state. We can use the adapter functions as reducers directly, so we'll pass `postsAdapter.addOne` as the reducer function to handle that action. In this case, we use the adapter method _as_ the actual reducer for this action.
11361155
11371156
Finally, we can replace the old hand-written `selectAllPosts` and `selectPostById` selector functions with the ones generated by `postsAdapter.getSelectors`. Since the selectors are called with the root Redux state object, they need to know where to find our posts data in the Redux state, so we pass in a small selector that returns `state.posts`. The generated selector functions are always called `selectAll` and `selectById`, so we can use destructuring syntax to rename them as we export them and match the old selector names. We'll also export `selectPostIds` the same way, since we want to read the list of sorted post IDs in our `<PostsList>` component.
11381157
1139-
We could even cut out a couple more lines by changing `postUpdated` to use the postsAdapter.updateOne`method. This takes an object that looks like`{id, changes}`, where `changes` is an object with fields to overwrite:
1158+
We could even cut out a couple more lines by changing `postUpdated` to use the `postsAdapter.updateOne` method. This takes an object that looks like`{id, changes}`, where `changes` is an object with fields to overwrite:
11401159
11411160
```ts title="features/posts/postsSlice.ts"
11421161
const postsSlice = createSlice({
@@ -1577,8 +1596,8 @@ We've built a lot of new behavior in this section. Let's see what how the app lo
15771596
15781597
<iframe
15791598
class="codesandbox"
1580-
src="https://codesandbox.io/embed/github/reduxjs/redux-essentials-example-app/tree/checkpoint-4-entitySlices/?codemirror=1&fontsize=14&hidenavigation=1&theme=dark&runonclick=1"
1581-
title="redux-essentials-example-app"
1599+
src="https://codesandbox.io/embed/github/reduxjs/redux-essentials-example-app/tree/b68c49bccc26768b913e6951b0b5cc3b759f3028?fontsize=14&hidenavigation=1&module=%2fsrc%2Ffeatures%2Fposts%2FpostsSlice.ts&theme=dark&runonclick=1"
1600+
title="redux-essentials-example"
15821601
allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb"
15831602
sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"
15841603
></iframe>

docs/tutorials/essentials/part-8-rtk-query-advanced.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,8 @@ export const NotificationsList = () => {
11751175
new: metadata.isNew,
11761176
})
11771177
// highlight-end
1178+
1179+
// omit rendering
11781180
}
11791181
}
11801182
```
7.04 KB
Loading
9.68 KB
Loading
136 KB
Loading
109 KB
Loading
205 KB
Loading
74.9 KB
Loading

0 commit comments

Comments
 (0)