未加星标

From Redux to Angular ngrx/store

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二04 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏

ngrx/store is an RxJS powered state management for Angular applications (inspired by Redux, but do not use it) and angular-redux/store is just bindings around the Redux API (uses Redux).

The choice between those two is really up to you, they both support Ahead of Time compilation and Redux devtools.

As it is a bit of a mind shift, this post explain the transition from Redux to ngrx/store where everything is an Observable!

Action

An action in Redux or ngrx/store is the same thing. An action describes the change to make to the store. While type is required on both Redux and ngrx/store, the Action signature is different. With Redux the Action object apart from type is really up to you but with ngrx/store you can only add an optional payload key.

Here is the ngrx/store Action signature:

export interface Action { type: string; payload?: any; } Reducers

Actions describe the fact that something happened, but don’t specify how the application’s state changes in response. This is the job of reducers. The reducer is a pure function that takes the previous state and an action, and returns the next state.

Once again Redux and ngrx/store uses the same concept. As reducers are pure functions, it is important not to mutate arguments (state or action) and to return a new reference of the state if something was modified.

import { ActionReducer, Action } from '@ngrx/store'; import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions'; const INITIAL_STATE = 0; export const counterReducer: ActionReducer<number> = (state: number = INITIAL_STATE, action: Action) => { switch (action.type) { case INCREMENT_COUNTER: return state + 1; case DECREMENT_COUNTER: return state - 1; default: return state; } } Creating the store

Redux and ngrx/store both provide the combineReducers utility to implement a common state shape, which is a plain javascript object containing “slices” of domain-specific data at each top-level key.

For instance if you want your state to have a list of users and the language of the client you could do something like that shape:

{ "users": [], "language": "" }

To create such a shape, you can use combineReducers and associate a key with a reducer:

import { StoreModule, combineReducers } from '@ngrx/store'; import { usersReducer } from './reducers/users'; import { languageReducer } from './reducers/language'; export function reducer(state: any, action: any) { return combineReducers({ users: usersReducer, language: languageReducer, })(state, action); } @NgModule({ imports: [ BrowserModule, StoreModule.provideStore(reducer), ] }) export class AppModule {} Store API GetState

With Redux, getState is a simple function that returns the current state tree of you application. The equivalent when using ngrx/store is to use the RxJS take operator . take emits only the first n items emitted by an Observable, so take(1) will return the first value only.


From Redux to Angular ngrx/store

Getting the entire state:

let currentState = {}; store .select(state => state) // select the entire state .take(1) .subscribe(state => currentState = state) console.log(currentState) // entire state

Getting a portion of the state:

let usersList = {}; store .select(state => state.users) // select a portion of the state .take(1) .subscribe(users => usersList = users) console.log(usersList) // users list

or

let usersList = {}; store .select('users') // select a portion of the state .take(1) .subscribe(users => usersList = users) console.log(usersList) // users list Dispatch

Redux and ngrx/store have the same concept of dispatch . dispatch is the only way to trigger a state change by dispatching an action.

import { Component } from '@angular/core'; import { REMOVE_USER } from '../..actions'; import { AppState, IUser } from '../../reducers'; @Component({ selector: 'users', templateUrl: 'users.html' }) export class UsersComponent { constructor( private store: Store<AppState>, ) {} removeUser(user: IUser) { this.store.dispatch({ type: REMOVE_USER, payload: user }); } } Subscribe

Redux store.subscribe takes one argument, the callback to be invoked any time an action has been dispatched. This is how with Redux you are aware of changes in the store. With ngrx/store it is really easy, you just need to subscribe to the Observable that select return.

store .select(state => state) // select the entire state .subscribe(state => { // called when something has changed }) Action creators

Action creators are function that return an Action . Both Redux and ngrx/store can leverage this pattern to create actions.

import { Action } from '@ngrx/store'; export const LOGIN = 'LOGIN'; export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; export const LOGIN_FAILED = 'LOGIN_FAILED'; export const login = (): Action => ({ type: LOGIN }); export const loginSuccess = (token, user): Action => ({ type: LOGIN_SUCCESS, payload: { token, user } }); export const loginFailed = (): Action => ({ type: LOGIN_FAILED });

Once you have declared all you action creators, you can easily call them within your components:

import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; import { Http } from '@angular/http'; import { login, loginSuccess, loginFailed } from './../actions'; @Component({ ... }) export class LoginComponent { constructor( public store: Store<AppState>, public http: Http, ) { } login(credentials = {}) { this.store.dispatch(login()); return this.http.post('authentication', credentials) .toPromise() .then(r => r.json()) .then((r) => this.store.dispatch(loginSuccess(r.access_token, r.user))) .catch((e) => this.store.dispatch(loginFailed())); } }

A cleaner way to do this is to use ngrx/effects .

Derived Data

To deal with derived data , Redux recommends to use Reselect . With ngrx/store you can use RxJS CombineLatest operator.

Let’s imagine that the following object is your application state:

{ "users": { "293580923": { "username": "shprink"}, "423948745": { "username": "byjc"}, "435435799": { "username": "myagoo"}, "027859645": { "username": "whoknows"}, ... }, "trendingUsers": [293580923, 435435799, ...], "topUsers": [423948745, 027859645] }

To get the trendingUsers or topUsers list of users you need to merge them with the actual users objects from the users key.

CombineLatest emits an item whenever any of the source Observables emits a new item.


From Redux to Angular ngrx/store

To get the list of trendingUsers with just CombineLatest operator, here is what you need to do:

Observable.combineLatest( store.select('users'), store.select('trendingUsers'), (users: IUsersState, trendingUsers: Array<number>) => trendingUsers.map(id => users[id]))

you can easily displayed the list of trendingUsers with the async pipe:

import { Store } from '@ngrx/store'; import { Observable } from "rxjs/Observable"; import 'rxjs/add/operator/combineLatest'; @Component({ template: ` <div *ngFor="let user of (usersStream$ | async)"> {{user.username}} </div> ` }) export class TrendingUsersComponent { usersStream$: Observable<Array<IUserState>>; constructor( public store: Store<AppState> ) { this.usersStream$ = Observable.combineLatest( store.select('users'), store.select('trendingUsers'), (users: IUsersState, trendingUsers: Array<number>) => trendingUsers.map(id => users[id])) } } Redux devtools

A cool thing with ngrx/store is that you can use Redux devtools for Chrome with ngrx/store-devtools .

import { StoreDevtoolsModule } from '@ngrx/store-devtools'; export function reducer(state: any, action: any) { ...} @NgModule({ imports: [ BrowserModule, StoreModule.provideStore(reducer), StoreDevtoolsModule.instrumentOnlyWithExtension(), ] }) export class AppModule {}

I hope you guys are now excited about ngrx/store, do not hesitate to leave a comment and you can also follow updates on twitter @julienrenaux

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: JavaChromeSUUCTI
分页:12
转载请注明
本文标题:From Redux to Angular ngrx/store
本站链接:http://www.codesec.net/view/534822.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(153)