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
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