Redxu(RTK) 基础 性能与数据范式化 6.1 添加用户页面
通过第5节,我们已经学会了使用thunk处理异步数据的整体流程了,在此基础上。
通过第6节,我们将要学会:
- 如何使用 createSelector 创建记忆化的 selector 函数
- 优化组件渲染性能的方法
- 如何使用 createEntityAdapter 来存储和更新范式化数据
简介
在第五节:异步逻辑与数据请求中,我们讲解了如何编写异步 thunks 从服务端 API 获取数据,如何处理异步请求 state 的开发模式,以及如何使用 selector 函数对从 Redux state 中读取数据的逻辑进行封装。
在本节中,我们将研究优化方法来确保我们的应用具有较好性能,以及用于自动处理 store 中数据常见更新的技术。
到目前为止,我们的大部分功能都以 posts 的特点为中心。接下来,我们将为这个应用添加几个新部分,添加这些之后,我们将看一看构建事物的具体细节,并讨论到目前为止我们构建的一些缺陷以及我们应该如何对其进行改进。
添加用户页面
让我们添加一个页面来显示所有用户列表,添加另一个页面来显示特定用户的所有帖子。(类似原来的PostsList组件嘛。。。)
先创建一个UserList组件,在他内部,我们也使用useSelector 从store获取数据并渲染成对应的用户列表,其中用户包含一个跳转到其各自页面的链接:
看下方代码捏!
//这是注释,显示文件路径捏:/src/features/users/UserList.tsx
import React from 'react'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import { selectAllUsers } from './usersSlice'
export const UsersList = () => {
const users = useSelector(selectAllUsers)
const renderedUsers = users.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`}>{user.name}</Link>
</li>
))
return (
<section>
<h2>Users</h2>
<ul>{renderedUsers}</ul>
</section>
)
}
当然不要忘记对应编写selectAllUsers 这个selector捏,另外再预先添加一个selectUserById,等会会用到的捏!
export const selectAllUsers = (state: RootState) => {
return state.users.users
};
export const selectUserById = (state: RootState, userId:string|undefined) =>
state.users.users.find(user => user.id === userId)
接下来,我们将添加
//这是注释,显示文件路径捏:/src/features/users/UserPage.tsx
import React from "react";
import { useSelector } from "react-redux";
import { Link } from "react-router-dom";
import { useParams } from "react-router";
import { selectUserById } from "../users/usersSlice";
import { selectAllPosts } from "../posts/postsSlice";
import { useAppSelector } from "../../app/hooks";
export const UserPage = ({}) => {
const { userId } = useParams();
const user = useAppSelector((state) => selectUserById(state, userId));
const postsForUser = useAppSelector((state) => {
const allPosts = selectAllPosts(state);
return allPosts.filter((post) => post.user === userId);
});
const postTitles = postsForUser.map((post) => (
<li key={post.id}>
<Link to={`/posts/${post.id}`}>{post.title}</Link>
</li>
));
return (
<section>
<h2>{user?.name}</h2>
<ul>{postTitles}</ul>
</section>
);
};
正如我们之前所见,我们可以通过调用 useSelector 或从 props 中获取数据,并在另一个 useSelector 中使用它来帮助决定从 store 中获取哪些内容。
这里我们的版本没使用props,原始文档版本如下:
export const UserPage = ({ match }) => {
const { userId } = match.params
//省略其他内容,仅仅为了不发生歧义,可见文档内容使用了match这个prop传入数据呢
上面的话的意思就是,我们在使用useSelector的时候可以动态的接受prop或者是其他数据来用于帮助编写useSelector接受的selector内部的逻辑。
比如外部prop传入某个值为真,那么某个selector内部可以有这样一条,我只选取store中某个数据切片中某个值为真的数据并返回这些数据呢。
在
//这是注释,显示文件路径捏:/src/App.tsx
<Routes>
<Route
path="/"
element={
<div>
<AddPostForm></AddPostForm>
<PostList />
</div>
}
/>
<Route
path="/posts/:postId"
element={<SinglePostPage></SinglePostPage>}
></Route>
<Route
path="/editPost/:postId"
element={<EditPostForm></EditPostForm>}
></Route>
<Route
path="/users"
element={<UsersList></UsersList>}
></Route>
<Route
path="/users/:userId"
element={<UserPage></UserPage>}
></Route>
<Route path="*" element={<div>This is nowhere</div>} />
</Routes>
然后在
//这是注释,显示文件路径捏:/src/components/Navbar.tsx
<div className="navLinks">
<Link to="/">文章列表</Link>
<Link to="/users">用户列表</Link>
</div>