背景:
团队正在使用 Angular 和用于状态管理的 @ngrx 库构建一个基于 REST 的大型 Web 应用程序。
我们希望将来自服务器的实体建模为 TypeScript 类。这些可能是:帐户、用户等
这实现了:
fullName不确定性在于时候,应用程序的时间表时,初始化模式,电话:new Account(accountResponse)。
传统逻辑建议尽早执行此操作,在服务中与检索帐户的逻辑(无论是从缓存、服务器响应等)一起使用。
this.apiService.fetch(AccountService.URL)
.map(accounts => accounts.map((a: AccountResponse) => new Account(a)));
Run Code Online (Sandbox Code Playgroud)
此方法由 ngrx 效果调用,然后在成功响应之后,Reducer 将 Account 对象添加到存储中。
然而,这是有效的...... ngrx / redux“最佳 实践”指出,为了便于序列化以及其他原因,应该只将普通对象和原语保存在商店中。
为了遵守这个建议,初始化 Account 对象必须在更远的地方进行。在单个组件中,在状态选择器中,或者通常在使用帐户的任何地方。
这对我来说没有意义,因为原始帐户响应对象是在应用程序中传递的,这在某种程度上违背了首先将它们包装在模型中的意义。
该应用程序在结构上类似于@ngrx/example book 应用程序,鉴于其简单性,它不会将服务器响应包装在模型对象中。
问题:
将初始化的类保留在商店中的不利影响是什么(除了可序列化)?
如果只将普通对象保存在商店中,那么在通过应用程序的数据流中,模型class最适合初始化的位置是什么?
我是 ngrx 的新手(并且从未使用过 redux)并且我试图理解这一切 - 特别是是否需要状态的深层副本。这是我到目前为止所学到的以及仍然让我感到困惑的内容(以粗体显示)。
该NGRX文档状态是
每个 reducer 函数都会获取最新调度的 Action、当前状态,并确定是返回新修改的状态还是原始状态。
他们还指出状态转换需要不变地发生——如果你改变状态,它需要在一个副本上:
每个动作都不变地处理状态转换。这意味着状态转换不会修改原始状态,而是使用扩展运算符返回一个新的状态对象。
但是,他们没有说明为什么这是必要的,除了促进引用完整性(我不太确定如何确保您的引用指向数据 - 我只知道关系数据库上下文中的术语 - 全部发挥作用):
spread 语法将属性从当前状态复制到对象中,创建一个新的引用。这确保了每次更改都会产生一个新状态,从而保持更改的纯度。这也促进了引用完整性,确保在发生状态更改时丢弃旧引用。
不过,那(参照完整性和上面的部分)并没有真正解释为什么这是必要的。
在 SO 的其他地方,我发现一条评论表明它允许将 Angular 的更改检测策略更改为OnPush.
(我也有点困惑,如果一个动作可以触发多个 reducer,结果状态副本可以合并,但这显然是由每个 reducer 专门处理单独的状态切片和 redux 意识到这一点来解释的。)
不过,重要的事情似乎是,状态的浅或深副本创建了一个新的 reference,这意味着 ngrx 正在向其订阅者推送更改:
如果返回的对象引用发生了变化,它将触发有关特定状态的任何相关 RxJS 状态订阅。使用一些好的 ngrx 选择器可以最小化触发哪些订阅。
一般来说,Redux FAQ列出了为什么不变性是一件好事以及为什么 redux 需要它:
提高性能、更简单的调试、更容易推理、更安全的数据处理、浅层相等性检查
他们还说它
使复杂的变化检测技术能够简单且廉价地实施,确保更新 DOM 的计算成本高昂的过程仅在绝对必须时发生
正如刚刚指出的(作为不变性的要求之一)redux 会进行浅层相等性检查。
然而,Nrgx 的文档推荐深度克隆(另外,如果副本引用旧对象,状态并不是真正不可变的,我想):
注意:spread 操作符只做浅拷贝,不处理深度嵌套的对象。您需要复制对象中的每个级别以确保不变性。有处理深度复制的库,包括 lodash 和 immer。 …
我正在尝试使用 RXJS 在 Angular 8 中使用 jest 创建单元测试。我想模拟商店。我正在使用https://ngrx.io/guide/store/testing使用模拟选择器的示例。谁能帮助我如何使用 MemoizedSelector 和 MockStore 独立使用模拟商店。
this.store.select(homeState).pipe(select(s => s.checkStatus)).subscribe(status => {
console.log(status);
// We performation other action.
});
Run Code Online (Sandbox Code Playgroud)
我在这个组件中有很多选择器。如何模拟许多选择器并更新每个测试用例的选择器值?
我一直在尝试创建一个示例应用程序来学习 ngrx,我的应用程序只有一个“操作”ADD_ITEM,并且它有一个减速器函数,该函数根据操作类型对状态进行操作。
购物.actions.ts
import { Action } from '@ngrx/store'
import {ShoppingItem} from '../models/shopping-item.model'
export enum ShoppingActionTypes{
ADD_ITEM = '[SHOPPING] Add Item'
}
export class AddItemAction implements Action {
readonly type = ShoppingActionTypes.ADD_ITEM
constructor(public payload: ShoppingItem) { }
}
export type ShoppingAction = AddItemAction;Run Code Online (Sandbox Code Playgroud)
购物.reducer.ts
// import { ShoppingAction, ShoppingActionTypes } from "../actions/shopping-actions";
import { ShoppingActionTypes, ShoppingAction } from "../actions/shopping.actions";
import { ShoppingItem } from '../models/shopping-item.model';
const initialState: Array<ShoppingItem> = [
{
id: "1775935f-36fd-467e-a667-09f95b917f6d",
name: 'Diet Coke',
}
];
export function …Run Code Online (Sandbox Code Playgroud)我正在ngrx 网站selectors上阅读有关内容,我偶然发现了.selectorsprops
ngrx 团队表示它已被弃用,但他们没有提供替代方案。我想在我的应用程序中使用selectorswith props。
除了使用带有 props 的选择器之外,还有其他选择吗?
如果有的话我该怎么办?
我试图在通过 NgRx 功能模块组合状态时注入功能减少器。
import { NgModule, InjectionToken } from '@angular/core';
import { StoreModule, ActionReducerMap } from '@ngrx/store';
import * as fromFeature from './reducers';
export const FEATURE_REDUCER_TOKEN = new InjectionToken<ActionReducerMap<fromFeature.State>>('Feature Reducers');
Run Code Online (Sandbox Code Playgroud)
我应该在这里返回什么?
export function getReducers(): ActionReducerMap<fromFeature.State> {
// map of reducers
return {
};
}
Run Code Online (Sandbox Code Playgroud)
我试过
export function getReducers(): ActionReducerMap<fromFeature.State> {
// map of reducers
return {
reducerA: FeatureAReducer
};
}
Run Code Online (Sandbox Code Playgroud)
但它给出了错误Object literal may only specified known properties。
其余模块代码:
@NgModule({
imports: [
StoreModule.forFeature('feature', FEATURE_REDUCER_TOKEN),
],
providers: [ …Run Code Online (Sandbox Code Playgroud) 在此文章有关NGRX 5 medium.com(2月13日),他们目前pipeable选择.这让我想起了rxjs中有关可管理选择器的内容,它们不仅可以通过'纯函数,兄弟'来证明,而且还可以通过函数的方式在不同的情况下声明和重用,而不必每次都使用map来调用letable功能.
所以我同意,这在rxjs中是一件好事,但为什么我们需要在ngrx中使用它 - 对于选择器.链接的文章显示以下示例:
import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
interface AppState {
count: number;
}
@Component({
selector: 'my-app',
template: `
<button (click)="increment()">Increment</button>
<div>Current Count: {{ count$ | async }}</div>
<button (click)="decrement()">Decrement</button>
<button (click)="reset()">Reset Counter</button>
`
})
export class MyAppComponent {
count$: Observable<number>;
constructor(private store: Store<AppState>) {
this.count$ = store.pipe(select('count'));
}
}
Run Code Online (Sandbox Code Playgroud)
所以我们现在打电话store.pipe(select(...));
而不是store.select(Selector);- 收益在哪里?为什么我应该更改我的代码以使用此行为或至少开始使用可管理选择器?
当使用valueChanges订阅Angular Abstract Control中的更改时,是否有必要?unsubscribe()
我经常这样做:
// this.form is a FormGroup within a Component.
this.form.valueChanges.subscribe(_ => {
console.log(this.form.value);
});
Run Code Online (Sandbox Code Playgroud)
但是我应该自己管理订阅吗(就像我通常所做的ngrx那样)?:
import { Subscription } from 'rxjs';
// this.subscription is ngrx Subscription.
this.subscription = this.form.valueChanges.subscribe(_ => {
console.log(this.form.value);
});
public ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
Run Code Online (Sandbox Code Playgroud)
我之前没有这样做的唯一原因是因为关于 Angular Forms 的教程、示例和文档通常会省略存储对订阅的引用,而是按原样使用valueChanges。
相反,ngrx 教程似乎强调了取消订阅以避免内存泄漏的重要性。
我们的 Web 应用程序加载 NgRx 存储中的值,这些值在初始状态下设置为 null。我正在寻找一种很好地过滤非空值的方法,因为我一直在组件中看到
this.myValue = this.store.pipe(
select(mySelector),
filter(notNull),
);
// where
const notNull = (data) => data !== null;
Run Code Online (Sandbox Code Playgroud)
我认为这个代码重复,在选择器级别处理这个会很棒。但我也愿意接受其他解决方案。
我尝试过的一件事是自定义createSelector:
import {createSelector, createSelectorFactory, defaultMemoize} from '@ngrx/store';
const isEqualOrNull = (a: any, b: any): boolean =>
a === b || b === null;
const createSelectorNotNull = createSelectorFactory((projectionFn) =>
defaultMemoize(projectionFn, isEqualOrNull));
Run Code Online (Sandbox Code Playgroud)
不幸的是,这个解决方案不能处理初始null值,只能处理更多的初始值。
这个看似常见的问题有什么好的解决方案吗?
我尝试使用覆盖 EntityData getAll() 函数的服务设置 NgRx Data,以便我可以调用 API。但是当我调用 EntityService.getAll() 时没有任何反应
entity-metadata.ts :EntityMetadataMap 的标准定义,其中我定义了一个名为 PageLoadData 的实体
import { EntityMetadataMap } from '@ngrx/data';
export const entityMetadata : EntityMetadataMap = {
PageLoadData: {
entityDispatcherOptions: {
optimisticUpdate: true
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的 AppModule.ts
import { AppResolver } from './app.resolver';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { EntityDataModule, EntityDataService } from '@ngrx/data';
import { StoreModule } from …Run Code Online (Sandbox Code Playgroud) ngrx ×10
angular ×5
ngrx-store ×3
redux ×2
rxjs ×2
angular8 ×1
immutability ×1
javascript ×1
mocking ×1
observable ×1
state ×1
typescript ×1
unit-testing ×1