繁体   English   中英

如何使用@ngrx/store 有效地重置状态?

[英]How can I effectively reset a state using @ngrx/store?

最近几天我似乎被这个问题困住了。

我们正在开发一个 Angular 2 应用程序,我需要为用户创建一个向导来填写表单。

我已经成功地使数据流过向导的每个步骤,并保存它以便自由来回移动。 但是,一旦提交表单,我似乎无法重置它。

我应该补充一点,每个组件都在墙后。 也许更好的解决方案是在 AppModule 中直接注入单例服务。 但我似乎无法让它发挥作用。

到目前为止,这是我的代码:

第1步

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { EventOption } from '../../../events/shared/event-option.model';
import { Store } from '@ngrx/store';
import { NewEventService } from '../shared/new-event.service';
import { Event } from '../../../events/shared/event.model';
import { FriendService } from '../../../friends/shared/friend.service';

@Component({
  selector: 'app-upload-images',
  templateUrl: './upload-images.component.html',
  styleUrls: ['../../../events/new-event/new-event.component.css']
})
export class UploadImagesComponent implements OnInit {
  form: FormGroup;
  private event;
  private images = [];

  constructor(
    private _store: Store<any>,
    private formBuilder: FormBuilder,
    private router: Router,
    private newEventService: NewEventService,
    private friendService: FriendService
  ) {
    _store.select('newEvent').subscribe(newEvent => {
      this.event = newEvent;
    })
  }

  ngOnInit() {
    this.initForm(this.event);
    if (this.event.counter === 0) {
      let friends = this.friendService.getFriends('58aaf6304fabf427e0acc08d');
      for (let friend in friends) {
        this.event.userIds.push(friends[friend]['id']);
      }
    }
  }

  initForm(event: Event) {
    this.images.push({ imageUrl: 'test0', voteCount: 0 });
    this.images.push({ imageUrl: 'test1', voteCount: 0 });
    this.images.push({ imageUrl: 'test2', voteCount: 0 });
    this.images.push({ imageUrl: 'test3', voteCount: 0 });
    this.form = this.formBuilder.group({
      firstImage: [this.event.length > 0 ? this.event.eventOption[0].imageUrl : null],
      secondImage: [this.event.length > 0 ? this.event.eventOption[1].imageUrl : null],
      thirdImage: [this.event.length > 0 ? this.event.eventOption[2].imageUrl : null],
      fourthImage: [this.event.length > 0 ? this.event.eventOption[3].imageUrl : null],
    })
  }

  next() {
    this.event.eventOptions = this.images;
    this.newEventService.updateEvent(this.event);
    this.router.navigate(['events/new-event/choose-friends']);
  }

}

第2步

import { Component, OnInit, Input } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { EventOption } from '../../../events/shared/event-option.model';
import { Store } from '@ngrx/store';
import { Event } from '../../shared/event.model';
import { NewEventService } from '../shared/new-event.service';
import { FriendService } from '../../../friends/shared/friend.service';
import { SearchPipe } from '../../../core/search.pipe';

@Component({
  selector: 'app-choose-friends',
  templateUrl: './choose-friends.component.html',
  styleUrls: ['../../../events/new-event/new-event.component.css', './choose-friends.component.css']
})
export class ChooseFriendsComponent implements OnInit {

  private searchTerm = '';
  private event;
  private friends = [];
  private friendsError = false;

  constructor(
    private _store: Store<any>,
    private formBuilder: FormBuilder,
    private router: Router,
    private newEventService: NewEventService,
    private friendService: FriendService
  ) {
    _store.select('newEvent').subscribe(newEvent => {
      this.event = newEvent;
    })
  }

  ngOnInit() {
    this.friends = this.friendService.getFriends('58aaf6304fabf427e0acc08d');
  }

  selectedFriend(friendId: string) {
    return this.friendService.selectedFriend(friendId, this.event.userIds);
  }

  toggleFriend(friendId: string) {
    return this.friendService.toggleFriend(friendId, this.event.userIds);
  }

  toggleAllFriends() {
    return this.friendService.toggleAllFriends(this.friends, this.event.userIds);
  }

  submit() {
    if (this.event.userIds.length > 0) {
      this.newEventService.resetEvent();
      this.router.navigate(['events/vote-events']);
    } else {
      this.friendsError = true;
    }
  }

  back() {
    this.newEventService.updateEvent(this.event);
    this.router.navigate(['events/new-event/upload-images']);
  }

}

活动服务

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Store, Action } from '@ngrx/store';
import { Event } from '../../../events/shared/event.model';
import { EventOption } from '../../../events/shared/event-option.model';
import { newEvent, newEventModel } from './new-event.reducer';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/find';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class NewEventService {
  public newEvent$: Observable<newEventModel>;

  constructor(private store: Store<newEventModel>) {
    this.newEvent$ = this.store.select('newEvent');
  }

  getEvent(event) {
    return this.store.dispatch({
      type: 'GET_EVENT',
      payload: event
    })
  }

  updateEvent(event) {
    return this.store.dispatch({
      type: 'UPDATE_EVENT',
      payload: event
    })
  }

  resetEvent() {
    return this.store.dispatch({
      type: 'RESET_EVENT',
    })
  }

}

事件减少器

import { EventOption } from '../../shared/event-option.model';
import { EventType } from '../../shared/event-type.model';
import { ActionReducer, Action } from '@ngrx/store';
import { Event } from '../../shared/event.model';
import { FriendService } from '../../../friends/shared/friend.service';

export interface newEventModel {
  eventOptions: EventOption[];
  eventTypeId: number,
  duration: number,
  comment: string,
  privacyId: number,
  isGlobal: boolean,
  id: string,
  userIds: string[],
  counter: number
}

let blankState: newEventModel = {
  eventOptions: [],
  eventTypeId: null,
  duration: 1440,
  comment: '',
  privacyId: 0,
  isGlobal: false,
  id: '',
  userIds: [],
  counter: 0
}

let initialState: newEventModel = {
  eventOptions: [],
  eventTypeId: null,
  duration: 1440,
  comment: '',
  privacyId: 0,
  isGlobal: false,
  id: '',
  userIds: [],
  counter: 0
}

export const newEvent: ActionReducer<newEventModel> = (state: newEventModel = initialState, action: Action) => {
  // return new state
  switch (action.type) {
    case 'GET_EVENT':
      return state;
    case 'UPDATE_EVENT':
      action.payload.counter = action.payload.counter + 1;
      return action.payload;
    case 'RESET_EVENT':
      return Object.assign({}, state, {
        eventOptions: [],
        eventTypeId: null,
        duration: 1440,
        comment: '',
        privacyId: 0,
        isGlobal: false,
        id: '',
        userIds: [],
        counter: 0
      });
    default:
      return state;
  }
}

如果需要,我可以提供一个有效的 plunkr,但我需要先创建它。

TLDR:如何重置@ngrx/store 上的状态?

感谢您提供的任何帮助!

您可以通过使用Object.assigninitialState所有属性复制到新对象,从而在您的减速器中将状态重置为initialState

export const newEvent: ActionReducer<newEventModel> = (state: newEventModel = initialState, action: Action) => {
  // return new state
  switch (action.type) {
    // ...
    case 'RESET_EVENT':
      return Object.assign({}, initialState);
    // ...
  }
}

减速机的注意事项

reducer 应该是一个纯函数,所以不应该修改参数。 您的UPDATE_EVENT需要稍作调整:

case 'UPDATE_EVENT':
  let counter = { counter: action.payload.counter + 1 };
  return Object.assign({}, action.payload, counter);

到后续的图案是Object.assign({}, source1, source2, ...)其中source1source2等包含要分配的属性。 在属性source1是由重复的属性覆盖source2等。

有更简单的方法,您只需要设置initialState而不是state

  const reducer = createReducer(initialState,
  on(proofActions.cleanAdditionalInsuredState, (state, action) => ({
    ...initialState
  })),

Noy Levi 对这个问题的回答是正确的,它将initialState 分配回状态,但是,有一种方法可以自动为每个reducer 分配initialState。

要理解的关键概念是,如果传递给减速器的“状态”的值是“未定义”(不是“空”,它需要是“未定义”),那么减速器将自动将提供给的初始状态分配给“状态”减速器创建时。 由于这种默认行为,您可以创建一个识别动作的“元减速器”,例如“注销”,然后将“未定义”状态传递给所有后续调用的减速器。

这篇关于 redux 的文章这篇关于 NgRx 的文章以及关于 NgRx 的这个答案中都很好地描述了这种行为。

相关代码如下所示:

export function logoutClearState(reducer) {
    return function (state, action) {
        if (action.type === ActionTypes.LOGOUT) {
            state = undefined;
        }
        return reducer(state, action);
    };
}

@NgModule({
    imports: [
        StoreModule.forRoot(reducers, { metaReducers: [logoutClearState] }),
    ],
    declarations: [],
    providers: [],
})

我假设您的 RESET_EVENT 应该返回一个新对象。 尽管您正在使用state数据和另一个对象填充对象:

case 'RESET_EVENT':
  return Object.assign({}, state, {
    eventOptions: [],
    eventTypeId: null,
    duration: 1440,
    comment: '',
    privacyId: 0,
    isGlobal: false,
    id: '',
    userIds: [],
    counter: 0
  });

Object.assign的语法是Object.assign(target, ...sources)并且您提供两个项目作为源: state和包含eventOptionseventTypeId等的对象。

相反,您需要返回Object.assign({}, initialState);

对不起,我请了一天假为了准备一些考试。 我最终通过执行以下操作来“解决”它:

....
case 'RESET_EVENT':
  action.payload.eventOptions = blankState.eventOptions;
  action.payload.eventTypeId = blankState.eventTypeId;
  action.payload.duration = blankState.duration;
  action.payload.comment = blankState.comment;
  action.payload.privacyId = blankState.privacyId;
  ....
  return action.payload;
....

它可能不是最漂亮或最好的解决方案,但至少它有效。 感谢@iblamefish 和所有人的帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM