Data/model 在 React Router / Redux 应用程序中管理
Data/model managing in React Router / Redux application
我想知道在使用 React Router
和 Redux
的 React 应用程序中加载服务器数据的最佳方法是什么。
例如:访问 /dossier/123
时,我只想检索该特定档案的数据。导航到 /dossiers 时,我想显示所有项目的 "some" 数据(标题、描述、所有者...)。
这意味着,我的模型看起来像这样:
{
dossiers: IDossier[];
}
interface IDossier {
id: number;
title: string;
description: string;
owner: IUser;
codes?: ICode[];
template?: ITemplate;
}
当 "entering" 应用程序处于 /dossier/123 时,"initial" 状态看起来像
{
dossiers: [{
id: 123,
title: "My dossier",
description: "...",
owner: { id: 1; name: "John Doe" },
codes: [{ ... }, { ... }],
template: { ... }
}]
}
然而,当进入 /dossiers 时,初始状态看起来更像
{
dossiers: [{
id: 123,
title: "My dossier",
description: "...",
owner: { id: 1; name: "John Doe" }
}, {
id: 456,
title: "Another dossier",
description: "...",
owner: { id: 2; name: "Jane Doe" }
}, {
...
}]
}
当然后导航到/dossier/123或/dossier/456时,我需要某种指示我必须加载代码和模板信息,因为它们是尚未获取 - 至少不是我第一次进入 url 端点。
所以我想我的主要问题是:我如何以稳健的方式管理它,而不需要 re-fetching 数据,因为它已经在商店中可用,在应用程序中来回导航时。
每次您需要加载某个档案实例或整个集合时,您应该首先查看存储中已有的数据并确定是否需要调用 API或不。由您决定。有些应用程序可能会在您每次打开页面时刷新数据,有些应用程序可能一天最多刷新一次。
如果有多个不同的数据集合具有相同的逻辑——例如,具有 id
的对象的数组首先被加载并且从不刷新——那么你应该对该逻辑进行抽象, 这样你就不需要重复自己了。
我建议使用函数式编程。更具体地说,higher-order 函数。
例如,制作一个通用的集合加载器。
// Collection loader “constructor”.
const createCollectionLoader = (store, selector, actionPrefix) => {
// Actual collection loader.
return {
// Function that makes sure collection is loaded.
loadAll: () => {
const collection = selector(store.getState());
const shouldLoad = …; // Decide if it should load.
if (shouldLoad) {
store.dispatch({ type: `${actionPrefix}_LOAD`, data });
try {
const data = …; // Load.
store.dispatch({ type: `${actionPrefix}_LOAD_SUCCESS`, data });
} catch (error) {
store.dispatch({ type: `${actionPrefix}_LOAD_ERROR`, error });
}
}
},
// Function that makes sure single collection item is loaded.
load: (id) => {
const collection = selector(store.getState());
const item = collection.find(item => item.id === id);
const shouldLoad = …; // Decide if it should load.
if (shouldLoad) {
store.dispatch({ type: `${actionPrefix}_ITEM_LOAD`, data });
try {
const data = …; // Load.
store.dispatch({ type: `${actionPrefix}_ITEM_LOAD_SUCCESS`, data });
} catch (error) {
store.dispatch({ type: `${actionPrefix}_ITEM_LOAD_ERROR`, error });
}
}
},
};
};
然后为您拥有的每个集合创建加载器。
const dossiersLoader = createCollectionLoader(store, state => state.dossiers, 'DOSSIERS');
最后,每次要确保加载文档(或加载单个文档)时,调用足够的加载器函数。
dossiersLoader.loadAll();
// or
dossiersLoader.load(id)
为了让它变得更好,引入一个表示工作单元的抽象——称之为任务、工作、流程、传奇等。
我更喜欢将我的 React Web 应用程序代码分成三个不同的组:UI、状态、逻辑。为此,我建议分别使用 react
(显然)、react-redux
和 redux-saga
。看到许多高阶组件、sagas 甚至 reducer 并不少见。
我想知道在使用 React Router
和 Redux
的 React 应用程序中加载服务器数据的最佳方法是什么。
例如:访问 /dossier/123
时,我只想检索该特定档案的数据。导航到 /dossiers 时,我想显示所有项目的 "some" 数据(标题、描述、所有者...)。
这意味着,我的模型看起来像这样:
{
dossiers: IDossier[];
}
interface IDossier {
id: number;
title: string;
description: string;
owner: IUser;
codes?: ICode[];
template?: ITemplate;
}
当 "entering" 应用程序处于 /dossier/123 时,"initial" 状态看起来像
{
dossiers: [{
id: 123,
title: "My dossier",
description: "...",
owner: { id: 1; name: "John Doe" },
codes: [{ ... }, { ... }],
template: { ... }
}]
}
然而,当进入 /dossiers 时,初始状态看起来更像
{
dossiers: [{
id: 123,
title: "My dossier",
description: "...",
owner: { id: 1; name: "John Doe" }
}, {
id: 456,
title: "Another dossier",
description: "...",
owner: { id: 2; name: "Jane Doe" }
}, {
...
}]
}
当然后导航到/dossier/123或/dossier/456时,我需要某种指示我必须加载代码和模板信息,因为它们是尚未获取 - 至少不是我第一次进入 url 端点。
所以我想我的主要问题是:我如何以稳健的方式管理它,而不需要 re-fetching 数据,因为它已经在商店中可用,在应用程序中来回导航时。
每次您需要加载某个档案实例或整个集合时,您应该首先查看存储中已有的数据并确定是否需要调用 API或不。由您决定。有些应用程序可能会在您每次打开页面时刷新数据,有些应用程序可能一天最多刷新一次。
如果有多个不同的数据集合具有相同的逻辑——例如,具有 id
的对象的数组首先被加载并且从不刷新——那么你应该对该逻辑进行抽象, 这样你就不需要重复自己了。
我建议使用函数式编程。更具体地说,higher-order 函数。
例如,制作一个通用的集合加载器。
// Collection loader “constructor”.
const createCollectionLoader = (store, selector, actionPrefix) => {
// Actual collection loader.
return {
// Function that makes sure collection is loaded.
loadAll: () => {
const collection = selector(store.getState());
const shouldLoad = …; // Decide if it should load.
if (shouldLoad) {
store.dispatch({ type: `${actionPrefix}_LOAD`, data });
try {
const data = …; // Load.
store.dispatch({ type: `${actionPrefix}_LOAD_SUCCESS`, data });
} catch (error) {
store.dispatch({ type: `${actionPrefix}_LOAD_ERROR`, error });
}
}
},
// Function that makes sure single collection item is loaded.
load: (id) => {
const collection = selector(store.getState());
const item = collection.find(item => item.id === id);
const shouldLoad = …; // Decide if it should load.
if (shouldLoad) {
store.dispatch({ type: `${actionPrefix}_ITEM_LOAD`, data });
try {
const data = …; // Load.
store.dispatch({ type: `${actionPrefix}_ITEM_LOAD_SUCCESS`, data });
} catch (error) {
store.dispatch({ type: `${actionPrefix}_ITEM_LOAD_ERROR`, error });
}
}
},
};
};
然后为您拥有的每个集合创建加载器。
const dossiersLoader = createCollectionLoader(store, state => state.dossiers, 'DOSSIERS');
最后,每次要确保加载文档(或加载单个文档)时,调用足够的加载器函数。
dossiersLoader.loadAll();
// or
dossiersLoader.load(id)
为了让它变得更好,引入一个表示工作单元的抽象——称之为任务、工作、流程、传奇等。
我更喜欢将我的 React Web 应用程序代码分成三个不同的组:UI、状态、逻辑。为此,我建议分别使用 react
(显然)、react-redux
和 redux-saga
。看到许多高阶组件、sagas 甚至 reducer 并不少见。