angular ngrx_angular ngrx存储操作选择器中的React性状态管理

谭越
2023-12-01

angular ngrx

NgRx framework helps to build reactive angular applications.

NgRx框架有助于构建React性角度应用程序。

  • NgRx Store provides reactive state management for the angular application. NgRx store is the redux implementation developed specifically for angular applications and provides RxJS observable API.

    NgRx Store为角度应用程序提供React状态管理。 NgRx存储是专门为角度应用程序开发的redux实现,并提供RxJS可观察的API。

  • The state is an immutable data structure that is a single source of truth for the whole application.

    状态是一个不变的数据结构,是整个应用程序的单一事实来源。

  • NgRx Actions represent the unique events in the application which may be used to perform state transition or trigger side-effects.

    NgRx动作表示应用程序中的唯一事件,可用于执行状态转换或触发副作用。

  • NgRx Reducers are pure functions that react to Actions to perform state transitions.

    NgRx Reducers是纯函数,对Actions做出React以执行状态转换。

  • NgRx Selectors are pure functions that select, derive, or compose a slice of the state.

    NgRx选择器是用于选择,派生或组成状态切片的纯函数。

  • NgRx Effects allow the isolation of side-effects.

    NgRx效果可隔离副作用。

先决条件 (Prerequisites)

  • you have a fair understanding of the angular framework.

    您对角度框架有相当的了解。
  • You have a basic understanding of redux architecture.

    您对redux架构有基本的了解。
  • you have a fair knowledge of RxJS Observable API and various operators.

    您对RxJS Observable API和各种运算符有一定的了解。

安装 (Installation)

If you already have an angular app, you can directly go to step — 4

如果您已经有一个角度应用程序,则可以直接转到步骤4

# 1) install angular cli
npm install -g @angular/cli


# 2) create a demo app
ng new ngrx-angular-demo


# 3) navigate to demo app
cd ngrx-angular-demo


# 4) install ngrx store
ng add @ngrx/store@latest


# 5) run angular in dev mode
ng serve

To begin with, let us have a look at an example file structure. A structure like this would be helpful to split up each feature of NgRx state management in your app. I usually replicate the same structure in each feature module.

首先,让我们看一个示例文件结构。 这样的结构有助于在应用程序中拆分NgRx状态管理的每个功能。 我通常在每个功能模块中复制相同的结构。

──store
        |_ app.actions.ts
        |_ app.effects.ts
        |_ app.reducer.ts
        |_ app.selectors.ts
  • app.actions.ts file will contain the NgRX actions

    app.actions.ts文件将包含NgRX actions

  • app.effects.ts file will contain the NgRx effects.

    app.effects.ts文件将包含NgRx effects

  • app.reducer.ts file will contain the State design and its initialization. it will also contain a reducer function.

    app.reducer.ts文件将包含State设计及其初始化。 它还将包含一个reducer函数。

  • app.selectors.ts will contain the NgRx selectors.

    app.selectors.ts将包含NgRx selectors

Here is the complete project setup.

这是完整的项目设置

(State)

The state represents an immutable object that contains the state of an application. It is read-only, so every state transition will return a new state rather than modifying the existing state. As the application grows, each feature should contain a separate state which are part of the global app state. As such, the application state contains one or more feature states.

状态代表包含应用程序状态的不可变对象。 它是只读的,因此每个状态转换都将返回新状态,而不是修改现有状态。 随着应用程序的增长,每个功能都应包含一个单独的状态,这些状态是全局应用程序状态的一部分。 这样,应用程序状态包含一个或多个功能状态。

The state is similar to Javascript objects. It contains the feature states as the key-value pairs where the key represent a feature state and the value is the feature state object.

状态类似于Javascript对象。 它包含功能状态作为key-value,其中key表示功能状态, value是功能状态对象。

The state related to a feature module is referred to as feature state.

与功能模块有关的状态称为feature state

interface State{
    feature_1: FeatureOneState,
    feature_2: FeatureTwoState,
    feature_3: FeatureThreeState
}

Let’s assume, our angular application has many feature modules. One of the features is responsible for the user’s profile. The profile module is responsible for rendering the list of users and the related posts .

假设我们的角度应用程序具有许多功能模块。 功能之一是负责用户的个人资料。 profile模块负责呈现users列表和相关posts

To design the state, we can assume that the state required for the profile module should contain a list of users and List of posts. Let’s call the profile state as ProfileFeatureState.

为了设计状态,我们可以假设个人档案模块所需的状态应包含用户列表和帖子列表。 让我们将概要文件状态称为ProfileFeatureState

/** User modal */
export interface User {


  id: number;
  email: string;
  first_name: string;
  last_name: string;
  avatar: string;
}


/** post modal **/
export interface Post {
  id: number;
  userId: number;
  title: string;
  body: string;
}




/** the modals should ideally be in their own ts file**/


export interface ProfileFeatureState {
  users: User[];
  posts: Post[];
}

We defined the type for User and Post and also created an interface for ProfileFeatureState.

我们为UserPost定义了类型,还为ProfileFeatureState创建了一个接口。

interface AppState{    
    profile: UserFeatureState,    
//..other features here
}

Finally, we would add ProfileFeatureStateto applications root state - AppState. The profile key represents the profileFeatureState.

最后,我们将增加ProfileFeatureState到应用程序根状态- AppStateprofile键表示profileFeatureState

初始化状态 (Initializing the state)

export const initialProfileFeatureState: ProfileFeatureState = {
  users: null,
  addresses: null
};

Initially, the state of the application is null since there would be no data. As such, both the users array and posts array would be initialized to null.

最初,由于没有数据,因此应用程序的状态为null 。 这样, users arrayposts array都将初始化为null

At this point, app.reducer.ts file should look like -

此时, app.reducer.ts文件应如下所示:

/*app.reducer.ts*/


/** User modal */
export interface User {
  id: number;
  email: string;
  first_name: string;
  last_name: string;
  avatar: string;
}


/** Post Modal */


export interface Post {
  id: number;
  userId: number;
  title: string;
  body: string;
}




export interface ProfileFeatureState {
  users: User[];
  addresses: Address[];
}


export const initialProfileFeatureState: ProfileFeatureState = {
  users: null,
  addresses: null
};


export interface AppState {
  profile: ProfileFeatureState;
}

NgRx动作(NgRx Actions)

NgRx Actions represent events in the application. They may trigger a state transition or trigger a side-effect in NgRx Effect services.

NgRx动作表示应用程序中的事件。 它们可能会触发状态转换或触发NgRx Effect服务中的NgRx Effect

The Action interface contains a property called Type. The Type property identifies the action. Actions can also contain optional metadata.

Action接口包含一个名为Type的属性。 Type属性标识动作。 操作还可以包含可选的metadata

interface Action{
    type:string
    //optional metadata properties
}

CreateAction函数(CreateAction function)

createAction function is used to create the actions and it returns an ActionCreator function. ActionCreator function, when called, returns an action of type TypedAction. Optionally, we can also supply additional metadata using the props function.

createAction function用于创建动作,它返回一个ActionCreator函数。 ActionCreator函数时,将返回TypedAction类型的TypedAction 。 (可选)我们还可以使用props函数提供其他元数据。

Let’s go ahead and create an action to add users to ProfileFeatureState.

让我们继续并创建一个将用户添加到ProfileFeatureState

export const addUsers = createAction(
 '[profile] add users',
 props<{ users: User[] }>()
);

Notice the type of addUsers action is [profile] add users. The [profile] represents the source of action. Also, the props contain the array of users as the metadata.

请注意, addUsers操作的类型为[profile] add users[profile]代表动作的来源。 而且,道具包含用户数组作为元数据。

Similarly, we can create an action for adding posts to the feature state.

同样,我们可以创建一个将帖子添加到功能状态的动作。

//file: app.actions.ts


export const addPosts = createAction(
  '[profile] add posts',
  props<{ posts: Post[] }>()
);

addPosts action is dispatched to indicate that the Posts should be added to the state. It will also contain Post[] metadata.

分派addPosts操作以指示应将Posts添加到状态。 它还将包含Post[]元数据。

Actions represent the events and not the commands or operations . A single command or operation may generate many types of Actions. For example: An operation which creates a new user would atleast generate Actions for success and failure such as [profile] user created or [profile] user creation failed .

动作代表事件,而不代表命令或操作。 单个命令或操作可能会生成多种类型的动作。 例如:创建新用户的操作至少会生成成功和失败的操作,例如[profile] user created[profile] user creation failed

NgRx减速器 (NgRx Reducers)

Reducers are pure functions that perform transitions from one state to another state based on the latest action dispatched. The reducer functions do not modify the existing state, rather it returns a new state for every state transition. Hence all the reducer functions perform immutable operations.

精简器是纯函数,可根据分派的最新操作执行从一种状态到另一种状态的转换。 Reducer函数不会修改现有状态,而是会为每个状态转换返回一个新状态。 因此,所有的reducer函数都执行不变的操作。

createReducer函数 (createReducer function)

NgRx provides a createReducer function to create reducers. It takes initialState as the first param and any number of on functions. The on function provides an association between actions and the state changes.

NgRx提供了createReducer函数来创建减速器。 它以initialState作为第一个参数以及any数量的on函数。 on功能提供了动作与状态更改之间的关联。

When an action is dispatched, all the reducers receive the action. The on function mapping determines whether the reducer should handle the action.

分派动作后,所有的减速器都会收到该动作。 on函数映射确定减速是否应该处理的动作。

createReducer function returns an ActionReducer function . ActionReducer function takes an Action and a State as input and returns a new computed State.

createReducer函数返回一个ActionReducer函数。 ActionReducer函数将一个Action和一个State作为输入,并返回一个新的已计算State。

Let’s go ahead a create reducer which handles transitions for ProfileFeatureState. createReducer function can map many actions and return an ActionReducer function.

让我们继续创建一个create reducer,它处理ProfileFeatureState转换。 createReducer函数可以映射许多动作并返回一个ActionReducer函数。

import * as AppActions from './app.actions';


const theProfFeatureReducer = createReducer(
  initialProfileFeatureState,
  on(AppActions.addUsers, (state, { users }) => ({
    ...state,
    users: [...users]
  })),
  on(AppActions.addPosts, (state, { posts }) => ({
    ...state,
    posts: [...posts]
  })),


);

The […] spread operator copies the properties of the object and returns a new object. It only performs the shallow copying and does not copy the nested structures. You should always consider a better alternative if you are dealing with a state that contains nested data structures. Libraries like lodash provide methods to clone nested structures.

[…] spread operator复制对象的属性并返回一个新对象。 它仅执行浅表复制,不复制嵌套结构。 如果要处理包含嵌套数据结构的状态,则应始终考虑使用更好的选择。 像lodash这样的提供了克隆嵌套结构的方法。

ActionReducerMap provides the mapping as key-value pairs where the key represents the feature name as a string and the value is the ActionReducer function returned by createReducer function.

ActionReducerMap将映射作为key-value对提供,其中key将功能名称表示为字符串,而valuecreateReducer函数返回的ActionReducer function函数。

In our case, the ActionReducerMap will contain profile as a key and value as theProfFeatureReducer.

在我们的示例中, ActionReducerMap将包含profile作为键,并将value包含在theProfFeatureReducer

/**The profileFeatureReducer function is necessary as function calls are not supported in the View Engine AOT compiler. It is no longer required if you use the default Ivy AOT compiler (or JIT)**/


function profileFeatureReducer
(state: ProfileFeatureState = initialState, action: Action) {
  return theProfFeatureReducer(state, action);
}


/** AppActionReducer Map**/
export const AppActionReducerMap: ActionReducerMap<AppState> = {
  profile: profileFeatureReducer
  // ... other feature go here


};

It is not necessary to create an ActionReducerMap. You can directly provide the mapping in StoreModule.forRoot({key: ActionReducer})while registering the reducer in app.module.ts. You can also separately register the feature state in the feature module. I prefer creating the ActionReducerMap separately as it provides a better type checking in Typescript.

不必创建ActionReducerMap 您可以在StoreModule.forRoot({key: ActionReducer})直接提供映射,同时在app.module.ts中注册化简器。 您也可以在功能模块中单独注册功能状态。 我更喜欢单独创建ActionReducerMap因为它在Typescript中提供了更好的类型检查。

At this point, our app.reducer.ts file should look like :

此时,我们的app.reducer.ts文件应如下所示:

/** app.reducer.ts **/
export interface ProfileFeatureState {
  users: User[];
  posts: Post[];
}


export const initialProfileFeatureState: ProfileFeatureState = {
  users: null,
  posts: null
};


export interface AppState {
  profile: ProfileFeatureState;
}


const theProfFeatureReducer = createReducer(
  initialProfileFeatureState,
  on(AppActions.addUsers, (state, { users }) => ({
    ...state,
    users: [...users]
  })),
  on(AppActions.addPosts, (state, { posts }) => ({
    ...state,
    posts: [...posts]
  }))
);




function profileFeatureReducer(state: ProfileFeatureState = initialProfileFeatureState, action: Action) {
  return theProfFeatureReducer(state, action);
}


export const AppActionReducerMap: ActionReducerMap<AppState> = {
  profile: profileFeatureReducer
};

注册州(Register the State)

Once the reducer is created, It should be registered in the Module.The state can be registered using one of the two options:

创建减速器后,应将其注册在模块中。可以使用以下两个选项之一来注册状态:

注册根状态 (Register Root state)

To register the global store in the application, StoreModule.forRoot() takes ActionReducerMap as an argument. The map contains key and ActionReducer Object returned by createReducer function.

要在应用程序中注册全局存储, StoreModule.forRoot()ActionReducerMap作为参数。 该映射包含由createReducer函数返回的keyActionReducer对象。

分别注册每个功能状态- (Register each feature state separately -)

Feature states are similar to root states but they represent the state of specific features of an application. Typically, each feature should be registered in its own module.

功能状态类似于根状态,但是它们表示应用程序特定功能的状态。 通常,每个功能都应在其自己的模块中注册。

//Register root state
 StoreModule.forRoot({ AppActionReducerMap })
 
 //register the feature state
  StoreModule.forFeature({ profile: profileFeatureReducer })

If you have come this far. You might also want to read about NgRx Selectors.

如果您走了这么远。 您可能还想阅读有关NgRx选择器的信息。

  • How to create NgRx Selectors

    如何创建NgRx选择器

  • How to use createSelector function to compose selectors with single or multiple slices of state

    如何使用createSelector函数组成具有单个或多个状态切片的选择器

  • How to use a projector function to return only a part within the slice of state.

    如何使用投影仪功能仅返回状态切片中的一部​​分

NgRx selectors are used to select a slice of state. I have a detailed post about it here.

NgRx选择器用于选择状态切片。 在这里有详细的帖子

This post was originally posted on initgrep.com

此帖子最初发布在initgrep.com上

翻译自: https://medium.com/initgrep/reactive-state-management-in-the-angular-ngrx-store-actions-selectors-50244ad65ea

angular ngrx

 类似资料: