createFeature NgRx - It Changes Everything

In this post I want to talk about new feature in NgRx which is called createFeature.

Starting from Angular 15 we get inside NgRx such feature which is called createFeature.

This is just a sugar to create a reducer which allows us to write much less code than previously.

Initial project

Initial project

Here I have a small project where we get a list of posts which we fetch from API. It is all done with NgRx this is why inside Redux Devtools we can see our actions.

Now let's look on our code.

export const initialState: PostsStateInterface = {
  isLoading: false,
  posts: [],
  error: null,
};

export const reducers = createReducer(
  initialState,
  on(PostsActions.getPosts, (state) => ({ ...state, isLoading: true })),
  on(PostsActions.getPostsSuccess, (state, action) => ({
    ...state,
    isLoading: false,
    posts: action.posts,
  })),
  on(PostsActions.getPostsFailure, (state, action) => ({
    ...state,
    isLoading: false,
    error: action.error,
  }))
),

Here we defined initialState for our posts page and a reducers where actions change our state.

Additionally to that we have a bunch of selectors to get this data in our component.

export const selectFeature = (state: AppStateInterface) => state.posts

export const isLoadingSelector = createSelector(
  selectFeature,
  state => state.isLoading
)
export const postsSelector = createSelector(
  selectFeature,
  state => state.posts
)
export const errorSelector = createSelector(
  selectFeature,
  state => state.error
)

Refactoring to createFeature

Now let's refactor our reducer code to the new version.

const postsFeature = createFeature({
  name: 'posts',
  reducer: createReducer(
    initialState,
    on(PostsActions.getPosts, (state) => ({ ...state, isLoading: true })),
    on(PostsActions.getPostsSuccess, (state, action) => ({
      ...state,
      isLoading: false,
      posts: action.posts,
    })),
    on(PostsActions.getPostsFailure, (state, action) => ({
      ...state,
      isLoading: false,
      error: action.error,
    }))
  ),
});

Here instead of reducers we created a postsFeature by calling createFeature function. Inside we provided a name and a reducer which has exactly the same code as previously. So the changes are minimal. But what are the benefits?

export const {
  name: postsFeatureKey,
  reducer: postsReducer,
  selectError,
  selectIsLoading,
  selectPosts,
} = postsFeature;

Here we can export from postsFeature a lot. We get a name and reducer that we can use everywhere but also we got 3 selectors. selectError, selectIsLoading and selectPosts.

The biggest benefit of createFeature is that we get all default selectors out of the box without need to create them.

Now we can completely remove our file selectors.ts as we get all selectors from createFeature and we don't need to write any of them.

We just need to update our component and use these selectors now.

import { selectError, selectIsLoading, selectPosts } from './store/reducers';

export class PostsComponent implements OnInit {
  isLoading$ = this.store.pipe(select(selectIsLoading));
  error$ = this.store.pipe(select(selectError));
  posts$ = this.store.pipe(select(selectPosts));

  ...
}

We must also update main.ts to use the correct reducer name.

import { postsReducer, postsFeatureKey } from './app/posts/store/reducers';

bootstrapApplication(AppComponent, {
  providers: [
    provideState(postsFeatureKey, postsReducer),
    ...
  ],
});

Here we imported postsReducer and postsFeatureKey which we got from createFeature.

Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!

📚 Source code of what we've done
Did you like my post? Share it with friends!
Don't miss a thing!
Follow me on Youtube, Twitter or Instagram.
Oleksandr Kocherhin
Oleksandr Kocherhin is a full-stack developer with a passion for learning and sharing knowledge on Monsterlessons Academy and on his YouTube channel. With around 15 years of programming experience and nearly 9 years of teaching, he has a deep understanding of both disciplines. He believes in learning by doing, a philosophy that is reflected in every course he teaches. He loves exploring new web and mobile technologies, and his courses are designed to give students an edge in the fast-moving tech industry.