如何使用 ngrx/entity 规范化深层嵌套数据(EntityState 和 EntityAdapter)
How to normalize deeply nested data with ngrx/entity (EntityState and EntityAdapter)
我希望对来自我的服务器的数据进行规范化,以便我可以更轻松地使用它 ngrx/entity。
我喜欢 ngrx/entity 通过提供 EntityState 接口和 EntityAdapter 来降低 reducer 和其他东西的复杂性。但是我发现它不适用于嵌套数据。
我有3个级别的数据:
训练 -> 练习 -> 组
如果我将它与 ngrx/entity 的经典模式一起使用,当我处理嵌套数据时它会很快变得拥挤。
下面是我 运行 使用 ngrx/entity 时的第一件事
之后我四处窥探并找到了 normalizr 库
我喜欢 normalizr 如何规范化我的数据,并仅使用 id 替换嵌套数组值作为其他实体(练习、集合)的键
我首先尝试的是组合多个实体状态,如下所示:
但这需要更改我的服务器以及大量逻辑和工作。
我想要以某种方式将 normalizr 与 ngrx/entity 结合起来。获得 normalizr 给我的相同东西,但可以自由使用 ngrx/entity 中的实体适配器 api 它是ngrx/entity
中为我服务的选择器和其他代码
所以最重要的是,我的问题是如何使用 ngrx/entity 规范化深度嵌套数据(就像 normalizr 库所做的那样)而不需要某种服务器的努力。
@ngrx/entity
不提供规范化数据的方法。您可以将 normalizr 与 @ngrx/entity
结合使用,例如,您可以在 Angular service/NgRx Effect/NgRx reducer 中规范化数据。
Redux 文档在 https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape.
中有一些关于在 redux 中使用规范器的页面
所以我找到了一些解决方案,同时仍在使用 NGRX
在开始之前,我只想说 ngrx 也有 ngrx/data 包,它提供的样板文件更少。但是当我阅读它时,我找到了我的问题的明确答案:
https://ngrx.io/guide/data/limitations
"This library shallow-clones the entity data in the collections. It doesn't clone complex, nested, or array properties. You'll have to do the deep equality tests and cloning yourself before asking NgRx Data to save data."
我相信 ngrx/entity 也是如此。
我开始寻找替代解决方案:BreezeJs、NGXS、Akita,我发现 NGXS 对我来说很快就能理解,但需要努力将我的 ngrx 实现与项目分离。
所以我回到 ngrx 并尝试为 3 层深度嵌套数据做一个解决方法
创建 3 个单独的实体状态(我将尝试使用 ngrx/data 可以肯定地减少所有样板文件)
创建一个函数,它将 return 所有必要的实体和每个实体的 ID(使用 normalizr 进行规范化)
export function normalizeTrainingArray(trainings: Training[]) {
var normalized = normalize(trainings, trainingsSchema);
var entities = {
trainings: {},
exercises: {},
sets: {}
}
entities.trainings = normalized.entities.trainings ? normalized.entities.trainings : {};
entities.exercises = normalized.entities.exercises ? normalized.entities.exercises : {};
entities.sets = normalized.entities.sets ? normalized.entities.sets : {};
var ids = {
trainingIds: [],
exerciseIds: [],
setIds: []
}
ids.trainingIds = normalized.entities.trainings ? Object.values(normalized.entities.trainings).map(x => x.id) : [];
ids.exerciseIds = normalized.entities.exercises ? Object.values(normalized.entities.exercises).map(x => x.id) : [];
ids.setIds = normalized.entities.sets ? Object.values(normalized.entities.sets).map(x => x.id) : [];
return {
entities,
ids
}
像这样就足够了。
发送 normalizeData 操作并使用 effect 调用此方法并为 fetchedData 分派 3 个不同的操作...
大致如下:
trainingsNormalized$ = createEffect(() =>
this.actions$.pipe(
ofType(TrainingActions.normalizeTrainings),
tap(payload => {
var normalized = normalizeTrainingArray(payload.trainings);
this.store.dispatch(TrainingActions.trainingsFetched({ entities: normalized.entities.trainings, ids: normalized.ids.trainingIds }))
this.store.dispatch(ExerciseActions.exercisesFetched({ entities: normalized.entities.exercises, ids: normalized.ids.exerciseIds }))
this.store.dispatch(SetActions.setsFetched({ entities: normalized.entities.sets, ids: normalized.ids.setIds }))
})
)
, { dispatch: false });
并且在一个示例减速器中:
// GET ALL
on(TrainingActions.trainingsFetched, (state: TrainingState, payload: { entities: Dictionary<Training>, ids: string[] }) => {
return {
...state,
entities: payload.entities,
ids: payload.ids
}
}),
结果是:
我认为有2种解决方案
使用 https://github.com/paularmstrong/normalizr 规范化您的数据
使用https://ngrx.io/guide/entity/adapter#adapter-collection-methods映射函数进行深度克隆。
return adapter.map(
entity => entity.id == obj.id ? {...entity, foo: 'bar'} : entity,
state
);
或者您可以使用 https://immerjs.github.io/immer/docs/introduction 来处理这个问题
return adapter.map(
entity => entity.id == obj.id ? produce(entity, (draft: any) => { draft.foo = 'bar' }) : entity,
state
);
我希望对来自我的服务器的数据进行规范化,以便我可以更轻松地使用它 ngrx/entity。
我喜欢 ngrx/entity 通过提供 EntityState 接口和 EntityAdapter 来降低 reducer 和其他东西的复杂性。但是我发现它不适用于嵌套数据。
我有3个级别的数据:
训练 -> 练习 -> 组
如果我将它与 ngrx/entity 的经典模式一起使用,当我处理嵌套数据时它会很快变得拥挤。
下面是我 运行 使用 ngrx/entity 时的第一件事
我首先尝试的是组合多个实体状态,如下所示:
我想要以某种方式将 normalizr 与 ngrx/entity 结合起来。获得 normalizr 给我的相同东西,但可以自由使用 ngrx/entity 中的实体适配器 api 它是ngrx/entity
中为我服务的选择器和其他代码所以最重要的是,我的问题是如何使用 ngrx/entity 规范化深度嵌套数据(就像 normalizr 库所做的那样)而不需要某种服务器的努力。
@ngrx/entity
不提供规范化数据的方法。您可以将 normalizr 与 @ngrx/entity
结合使用,例如,您可以在 Angular service/NgRx Effect/NgRx reducer 中规范化数据。
Redux 文档在 https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape.
中有一些关于在 redux 中使用规范器的页面所以我找到了一些解决方案,同时仍在使用 NGRX
在开始之前,我只想说 ngrx 也有 ngrx/data 包,它提供的样板文件更少。但是当我阅读它时,我找到了我的问题的明确答案:
https://ngrx.io/guide/data/limitations "This library shallow-clones the entity data in the collections. It doesn't clone complex, nested, or array properties. You'll have to do the deep equality tests and cloning yourself before asking NgRx Data to save data."
我相信 ngrx/entity 也是如此。
我开始寻找替代解决方案:BreezeJs、NGXS、Akita,我发现 NGXS 对我来说很快就能理解,但需要努力将我的 ngrx 实现与项目分离。
所以我回到 ngrx 并尝试为 3 层深度嵌套数据做一个解决方法
创建 3 个单独的实体状态(我将尝试使用 ngrx/data 可以肯定地减少所有样板文件)
创建一个函数,它将 return 所有必要的实体和每个实体的 ID(使用 normalizr 进行规范化)
export function normalizeTrainingArray(trainings: Training[]) {
var normalized = normalize(trainings, trainingsSchema);
var entities = {
trainings: {},
exercises: {},
sets: {}
}
entities.trainings = normalized.entities.trainings ? normalized.entities.trainings : {};
entities.exercises = normalized.entities.exercises ? normalized.entities.exercises : {};
entities.sets = normalized.entities.sets ? normalized.entities.sets : {};
var ids = {
trainingIds: [],
exerciseIds: [],
setIds: []
}
ids.trainingIds = normalized.entities.trainings ? Object.values(normalized.entities.trainings).map(x => x.id) : [];
ids.exerciseIds = normalized.entities.exercises ? Object.values(normalized.entities.exercises).map(x => x.id) : [];
ids.setIds = normalized.entities.sets ? Object.values(normalized.entities.sets).map(x => x.id) : [];
return {
entities,
ids
}
像这样就足够了。 发送 normalizeData 操作并使用 effect 调用此方法并为 fetchedData 分派 3 个不同的操作...
大致如下:
trainingsNormalized$ = createEffect(() =>
this.actions$.pipe(
ofType(TrainingActions.normalizeTrainings),
tap(payload => {
var normalized = normalizeTrainingArray(payload.trainings);
this.store.dispatch(TrainingActions.trainingsFetched({ entities: normalized.entities.trainings, ids: normalized.ids.trainingIds }))
this.store.dispatch(ExerciseActions.exercisesFetched({ entities: normalized.entities.exercises, ids: normalized.ids.exerciseIds }))
this.store.dispatch(SetActions.setsFetched({ entities: normalized.entities.sets, ids: normalized.ids.setIds }))
})
)
, { dispatch: false });
并且在一个示例减速器中:
// GET ALL
on(TrainingActions.trainingsFetched, (state: TrainingState, payload: { entities: Dictionary<Training>, ids: string[] }) => {
return {
...state,
entities: payload.entities,
ids: payload.ids
}
}),
结果是:
我认为有2种解决方案
使用 https://github.com/paularmstrong/normalizr 规范化您的数据
使用https://ngrx.io/guide/entity/adapter#adapter-collection-methods映射函数进行深度克隆。
return adapter.map(
entity => entity.id == obj.id ? {...entity, foo: 'bar'} : entity,
state
);
或者您可以使用 https://immerjs.github.io/immer/docs/introduction 来处理这个问题
return adapter.map(
entity => entity.id == obj.id ? produce(entity, (draft: any) => { draft.foo = 'bar' }) : entity,
state
);