---
title: "How to Implement ActionCreationGroup in NgRx"
date: "2024-06-03"
slug: "how-to-implement-actioncreationgroup-in-ngrx"
author: "Dany Paredes"
canonical: "https://danywalls.com/how-to-implement-actioncreationgroup-in-ngrx"
description: "When you create actions in NgRx, you usually use the createAction function helper. We write our action names with an inline string about the source and the event, like [Cart] Update Price. This action is clearly linked with the Cart feature in our ap..."
---

When you create actions in NgRx, you usually use the `createAction` function helper. We write our action names with an inline string about the source and the event, like `[Cart] Update Price`. This action is clearly linked with the Cart feature in our application and the event to update the price. However, this action is not used alone; it will be used in several places, like the component, reducer, service, or effects. What happens if I change the name or have a typo?

I must update it in every place. For example, we have the action `'[Home Page] Accept Terms'`, which is declared in the `home.reducer.ts`.

```typescript
createAction('[Home Page] Accept Terms'), (state) => ({
    ...state,
    acceptTerms: !state.acceptTerms,
  })
```

But the `homeReducer`, include other actions related to the home feature:

```typescript
export const homeReducer = createReducer(
  initialState,
  on(createAction('[Home Page] Accept Terms'), (state) => ({
    ...state,
    acceptTerms: !state.acceptTerms,
  })),
  on(createAction('[Home Page] Reject Terms'), (state) => ({
    ...state,
    acceptTerms: false,
  })),
);
```

Those actions are triggered by the `home.component`, so we need to repeat the same code to dispatch the actions:

```typescript
onChange() {
    this._store.dispatch({
      type: '[Home Page] Accept Terms',
    });
  }
  onRejectTerms() {
    this._store.dispatch({
      type: '[Home Page] Reject Terms',
    });
  }
```

We need to be careful with the action description because it is the only way to make the [ReduxDevTools](https://www.danywalls.com/how-to-debug-ngrx-using-redux-devtools) provide information about the REDUX actions in our application.

![](/images/posts/how-to-implement-actioncreationgroup-in-ngrx/9a1c73c3-7ea7-4_71978.gif)

Why do I need to repeat the code everywhere? How do I group my actions related to a feature? I have good news for you: the `createActionGroup` function allows us to easily create actions related to a feature or section. Let's explore how to use it!

### Actions In NgRx

The actions help us describe unique events in our application. Each action has a unique string that represents the source and the event that was triggered. For example:

```typescript
export const playersLoad = createAction('[Players API] Players Load')
```

In other cases, we have a second optional parameter, `props`, which helps by providing a strongly typed parameter.

```typescript
export const playersLoadedSuccess = createAction('[Players API] Players Loaded Success', props<{ players: Array<Player>}>)
```

Instead of having separate actions, we can use the `createActionGroup` function to create a group of actions. The `createActionGroup` function takes an object as a parameter, which includes the source and the list of events (our actions).

For example, let's group all actions related to the players' page. Instead of repeating the action name for each one, we import `createActionGroup`, set the source, and declare the events (the actions). For actions without parameters, use the `emptyProps()` function, and for actions with parameters, use the `props` function.

```typescript
import { createActionGroup, emptyProps, props } from '@ngrx/store';

export const playersLoaded = createActionGroup({
  source: 'Players Page',
    events: {
   		'Players Load': emptyProps(),
	    'Player Loaded Success': props<{ players: Array<any> }>()  
    },
});
```

I highly recommend reading more about [Action Groups](https://ngrx.io/guide/store/action-groups) or taking a minute to watch the amazing @[Brandon Roberts](@brandonroberts) video [https://www.youtube.com/watch?v=rk83ZMqEDV4](https://www.youtube.com/watch?v=rk83ZMqEDV4).

## Using createActionGroup

It's time to start using `createActionGroup` in our project. We continue with the initial [**project of NgRx**](https://www.danywalls.com/understanding-when-and-why-to-implement-ngrx-in-angular), clone it, and switch to the `implement-store` branch.

```bash
git clone https://github.com/danywalls/start-with-ngrx.git
git switch implement-store
```

Open the project with your favorite editor, and create a `src/pages/home/state/home.actions.ts` file. Import the `createActionGroup` function to declare the `'Accept Terms'` and `'Reject Terms'` actions. Add two more actions for `loadPlayers` and `loadPlayerSuccess`, using the `emptyProps` and `props` functions.

The final code looks like this:

```typescript
import { createActionGroup, emptyProps, props } from '@ngrx/store';

export const HomePageActions = createActionGroup({
  source: 'Home Page',
  events: {
    'Accept Terms': emptyProps(),
    'Reject Terms': emptyProps(),
    'Players Load': emptyProps(),
    'Player Loaded Success': props<{ players: Array<any> }>(),
  },
});
```

First, refactor the `home.reducer.ts` file by moving the `HomeState` interface and `initialState` to `home.state.ts`. Add two new properties, `loading` and `players`, and initialize them in the `HomeState`.

```typescript
export interface HomeState {
  acceptTerms: boolean;
  loading: boolean;
  players: Array<any>;
}
export const initialState: HomeState = {
  acceptTerms: false,
  loading: false,
  players: [],
};
```

Next, create a new file `home.actions.ts`, import the `createActionGroup` function helper, and declare and export `HomePageActions`. This will contain all events (actions) related to the `HomePage`.

The final code in `home.actions.ts` looks like this:

```typescript
import { createActionGroup, emptyProps, props } from '@ngrx/store';

export const HomePageActions = createActionGroup({
  source: 'Home Page',
  events: {
    'Accept Terms': emptyProps(),
    'Reject Terms': emptyProps(),
    'Players Load': emptyProps(),
    'Player Loaded Success': props<{ players: Array<any> }>(),
  },
});
```

It's time to refactor `home.reducer.ts` by importing `HomePageActions` from `home.actions.ts`.

```typescript
import { HomePageActions } from './home.actions';
```

Remove the inline string actions and replace them with `HomePageActions` like this:

```typescript
  on(HomePageActions.acceptTerms, (state) => ({
    ...state,
    acceptTerms: !state.acceptTerms,
  })),
  on(HomePageActions.rejectTerms, (state) => ({
    ...state,
    acceptTerms: false,
  })),
```

Add new listeners for the `playersLoad` and `playerLoadSuccess` actions:

```typescript
on(HomePageActions.playersLoad, (state) => ({
    ...state,
    loading: true,
  })),
  on(HomePageActions.playerLoadedSuccess, (state, { players }) => ({
    ...state,
    loading: false,
    players,
  })
```

The final code in `home.reducer.ts` looks like:

```typescript
import { createReducer, on } from '@ngrx/store';
import { initialState } from './home.state';
import { HomePageActions } from './home.actions';

export const homeReducer = createReducer(
  initialState,
  on(HomePageActions.acceptTerms, (state) => ({
    ...state,
    acceptTerms: !state.acceptTerms,
  })),
  on(HomePageActions.rejectTerms, (state) => ({
    ...state,
    acceptTerms: false,
  })),
  on(HomePageActions.playersLoad, (state) => ({
    ...state,
    loading: true,
  })),
  on(HomePageActions.playerLoadedSuccess, (state, { players }) => ({
    ...state,
    loading: false,
    players,
  })),
);
```

## Using Actions

It's time to use `HomeActions` in the `home.component.ts`. Instead of dispatching string actions, first import:

```javascript
import { HomePageActions } from './state/home.actions';
```

Then, update the `this._store.dispatch` to use the `HomeActions`.

```typescript
  onChange() {
    this._store.dispatch(HomePageActions.acceptTerms());
  }
  onRejectTerms() {
    this._store.dispatch(HomePageActions.rejectTerms());
  }
```

Perfect, but we have two more actions, `loading` and `players`. The idea is to show a loading indicator and load a list of fake players.

Use the `store` to select the home slice and pick the `loading` and `players`.

> Yes, a better way is using selectors. We are going to play with them soon.

```typescript
public $loading = toSignal(this._store.select((state) => state.home.loading));
public $players = toSignal(
  this._store
    .select((state) => state.home.players)
);
```

We want to enter `home.component` and dispatch the `playersLoad` action in the `ngOnInit` lifecycle hook. Then, after 5 seconds, dispatch the `playerLoadSuccess` action with a list of fake players.

The code in `home.component.ts` looks like this:

```typescript
public ngOnInit(): void {
  this._store.dispatch(HomePageActions.playersLoad());

  setTimeout(() => {
    this._store.dispatch(
      HomePageActions.playerLoadedSuccess({
        players: [
          { id: 1, name: 'Lebron', points: 25 },
          { id: 1, name: 'Curry', points: 35 },
        ],
      }),
    );
  }, 5000);
}
```

Finally, we listen for the `$loading` signals and iterate over the `$players` using `@for`. The code looks like this:

```html
<div>
@if (!$loading()) {
    <h3>TOP NBA Players</h3>
    @for (player of $players(); track player) {
      <p>{{ player.name }} {{ player.points }}</p>
    }
  } @else {
    <span>Loading..</span>
  }
</div>
```

Save your changes and run the app using `ng serve`. Open the REDUX DevTools, and you will see the `playersLoad` action is triggered, and the loading message appears. After 5 seconds, the `PlayersLoadedSuccess` action is dispatched, and the list of players is shown.

![](/images/posts/how-to-implement-actioncreationgroup-in-ngrx/e225be35-614b-4_71978.gif)

## Conclusion

We learned how to share **NgRx** actions using the `createActionGroup` function to avoid repetitive code and reduce the risk of typos. We also learned how to group related actions and refactor our reducer and component code by grouping actions. This makes it easier to maintain our actions.

> Source Code: [https://github.com/danywalls/start-with-ngrx/tree/action-creators](https://github.com/danywalls/start-with-ngrx/tree/action-creators)

