我正在使用React / Redux创建一个应用程序,并且对使用React和Redux还是陌生的。我一直在试图弄清楚如何构造redux存储。这是我要实现的目标的简要说明,使用redux createEntityAdapter docs中的书籍示例进行了概括
我有一个带有书单的页面。用户可以单击一本书或多本书,并为每本书显示章节列表。当新的或缺少的章节在服务器上可用时,可以随时添加。添加的章节需要以正确的位置添加到列表中。
书籍列表是一个组件,而章节列表是另一个组件,但是由于书籍很多,因此章节列表组件的实例很多。每个组件都有自己的片段。
商店看起来像:
bookList : [ Book1, Book2, Book3, ...]
ChapterList: {
Book1 : {
chapters: [
{
Chapter : 'Chapter1',
info: 'Chapter info',
other: 'more stuff'
},
{
Chapter : 'Chapter2',
info: 'Chapter info',
other: 'more stuff'
},
{...}
],
bookInfo: 'info about book'
},
Book2 : { ... },
Book3 : { ... }
}
问题在于章节信息正在流式传输,因此不断更新,丢失的章节可能随时到达。因此,每次需要新数据到达时,我都需要对Chapters数组进行排序,但是该数组不是很长,大约50个元素,新数据每秒可以到达几次。
我打算使用createEntityAdapter来规范Chapters数组,但是鉴于将其嵌套在Book#属性中并且需要Book#属性,因此我看不到这是怎么可能的,因为我使用的是ChapterList组件。
const ChapterListAdapter = createEntityAdapter({
// where bookNum needs to Book1, Book2, Book3 etc...
selectId: (chapterList) => chapterList[bookNum].chapters,
sortComparer: (a, b) => a.chapters.localeCompare(b.chapters
})
我该如何解决?修改商店?有没有一种方法可以使ChapterList变平,使章节位于顶层?我是否将自己的归一化/排序逻辑写入到reducer中?
答案 0 :(得分:1)
有没有办法将chapterList展平,使章节位于顶层?我是否将自己的规范化/排序逻辑写入减速器?
您可以查看 Normalizing State Shape 上的 Redux 指南作为起点。
这里有两个实体:“书籍”和“章节”。它们之间存在一种关系:每一章都属于一本书,每本书都包含一个有序的章节列表。您需要两个实体适配器。你可以用一两片——用两片可能更容易,但没关系。
标准化状态形状应该如下所示:
{
books: {
ids: [1, 2, 3],
entities: {
1: {
id: 1,
title: "Book 1",
bookInfo: 'info about book',
chapterIds: [75, 962, 64], // an ordered array of chapters
},
2: { /* ... */ },
3: { /* ... */ }
}
},
chapters: {
ids: [64, 75, 962],
entities: {
64: {
id: 64,
name: "Some Chapter Title",
info: "",
other: "",
bookId: 1 // the id of the book that this chapter belongs to
},
75: { /* ... */ },
962: { /* ... */ }
}
}
}
在打字稿方面:
import { EntityState, EntityId } from "@reduxjs/toolkit";
export interface Book {
id: EntityId;
title: string;
bookInfo: string;
chapterIds: EntityId[];
}
export interface Chapter {
id: EntityId;
name: string;
info: string,
other: string;
bookId: EntityId;
}
interface State {
books: EntityState<Book>;
chapters: EntityState<Chapter>;
}
我很难在不知道数据来自服务器时的形状的情况下编写您的减速器。看了一章一章的内容,好像已经正常化了?
我更容易编写组件。我们有选择器,可以从 id 中选择完整的实体对象:
const bookAdapter = createEntityAdapter<Book>();
const chapterAdapter = createEntityAdapter<Chapter>();
export const {
selectIds: selectBookIds,
selectById: selectBookById
} = bookAdapter.getSelectors((state: State) => state.books);
export const {
selectById: selectChapterById
} = chapterAdapter.getSelectors((state: State) => state.chapters);
所以每个组件唯一需要的 prop 是 id
:
const Chapter = ({ id }: { id: EntityId }) => {
const chapter = useSelector((state) => selectChapterById(state, id));
// probably some hook here to request the chapter if not loaded
// dispatch an async thunk inside of a useEffect
if (!chapter) {
return <Loading />;
}
return (
<div>
<h3>{chapter.name}</h3>
<p>{chapter.info}</p>
</div>
);
};
const Book = ({ id }: { id: EntityId }) => {
const book = useSelector((state) => selectBookById(state, id));
// probably some hook here to request the book if not loaded
// dispatch an async thunk inside of a useEffect
if (!book) {
return <Loading />;
}
return (
<div>
<h1>{book.title}</h1>
<p>{book.bookInfo}</p>
<h2>Chapters</h2>
<ul>
{book.chapterIds.map((chapterId) => (
<li key={chapterId}>
<Chapter id={chapterId} />
</li>
))}
</ul>
</div>
);
};
您可以check out this answer了解有关如何仅在实体尚未加载时从 API 请求实体的更多信息。