platform icon indicating copy to clipboard operation
platform copied to clipboard

PoC: Schematics to create a main feature reducer file

Open moniuch opened this issue 2 years ago • 14 comments

I would like to ask you to consider providing a schematic which I don't believe exists yet, but which to me seems like the missing bit in the puzzle.

The artifact of the schematic would be a main feature reducer file (say, [name].feature-reducer.ts) holding an empty state interface, and an empty reducer map. This would be the home for the slices to come, this would be the file which you would reference in the feature schematic (ie. the --reducers parameter).

An example of the output would be (let me base on the code from SO):

export interface UserModuleState {}
export interface State extends fromRoot.State {}
export const reducers = {};

export const selectUserModuleState = createFeatureSelector<UserModuleState>('userModule');

This would be a home for the future slices: search, detail, detailBase, which you would add using every time using @ngrx/schematics:feature --reducers user.feature-reducer.ts, and end up with:

export interface UserModuleState {
  search: fromSearch.State;  
  detail: fromUserDetail.State;
  detailBase: fromDetailBase.State;
}

export interface State extends fromRoot.State {
    userModule: UserModuleState;    
}

export const reducers = {    
    search: fromSearch.reducer,
    detail: fromUserDetail.reducer,
    detailBase : fromDetailBase.reducer
};

// ...
// + suitable edits in the declaring module

IMO, there is no schematic available currently that produces this code. Having it available would make the whole store-building flow very intuitive and free from guesswork:

  • first, you create feature-reducer: ng generate @ngrx/schematics:feature-reducer which generates [name].feature-reducer.ts
  • then for each slice, a feature composed into feature reducer: ng generate @ngrx/schematics:feature --reducers [name].feature-reducer.ts

Currently, from what I was able to find out in my experiments, setting up a feature reducer requires manual work and model-defining hassle.

If accepted, I would be willing to submit a PR for this feature

[ ] Yes (Assistance is provided if you need help submitting a pull request) [ ] No

moniuch avatar Jan 26 '22 15:01 moniuch

Just wanted to share with you my PoC results and thus maybe convince this schematic would be helpful:

With the admin.feature-reducer.ts file created manually, containing the following skeleton code,

admin.feature.reducer.ts

import { ActionReducerMap, createFeatureSelector } from "@ngrx/store";
import * as fromRoot from '@myorg/app-config';

export const ADMIN_FEATURE_KEY = 'admin';

export interface State extends fromRoot.State {
  admin: AdminModuleState;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface AdminModuleState {}

export const reducers: ActionReducerMap<AdminModuleState> = {};

export const selectUserModuleState = createFeatureSelector<AdminModuleState>('adminModule');

I ran ng generate @ngrx/schematics:feature --reducers admin.feature-reducer.ts 3x to create 3 slices: and the result is quite pleasing (although still requiring minor touchups)

image 2022-01-26 18-14-54

admin.feature-reducer.ts

import { ActionReducerMap, createFeatureSelector } from "@ngrx/store";
import * as fromRoot from '@myorg/app-config';
import * as fromSlice1 from './slice1/slice1.reducer';
import * as fromSlice2 from './slice2/slice2.reducer';
import * as fromSlice3 from './slice3/slice3.reducer';

export const ADMIN_FEATURE_KEY = 'admin';

export interface State extends fromRoot.State {
  admin: AdminModuleState;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface AdminModuleState {
  [fromSlice1.slice1FeatureKey]: fromSlice1.State;
  [fromSlice2.slice2FeatureKey]: fromSlice2.State;
  [fromSlice3.slice3FeatureKey]: fromSlice3.State;
}

export const reducers: ActionReducerMap<AdminModuleState> = {
  [fromSlice1.slice1FeatureKey]: fromSlice1.reducer,
  [fromSlice2.slice2FeatureKey]: fromSlice2.reducer,
  [fromSlice3.slice3FeatureKey]: fromSlice3.reducer,
};

export const selectUserModuleState = createFeatureSelector<AdminModuleState>(ADMIN_FEATURE_KEY);

I only noticed some minor flaws in the feature schematics, although very easy to fix:

  1. The feature (= slice) reducer is placed in the main State interface, rather than AdminModuleState

feature-reducer-after-g-feature

  1. The declaring module is updated with the slice reducer, although it is not necessary, because it is already included in the module reducer map. The effects though are ok, they need to stay.

declaring-ng-module

A very quick fix. Could be, more surprises will come up as I continue with it. Otherwise the setup looks very good, so I think the feature-reduce schematic is worth consideration. Please let me know what you think.

moniuch avatar Jan 26 '22 17:01 moniuch

I think that the reducers schematics do exactly what you're looking for.

npx ng generate @ngrx/schematics:reducer customers --creators=true --group=true --path=src/app/store/reducers --reducers index.ts --api true

timdeschryver avatar Mar 04 '22 19:03 timdeschryver

In this command, you are adding the generated reducer to --reducers index.ts so you have this file ready. My ticket is about providing a way to create this sort of index file for a feature module which is expected to contain multiple slices.

This would be the home for the slices to come, this would be the file which you would reference in the feature schematic (ie. the --reducers parameter).

May I know how do create index.ts within a feature module which you know will host multiple slices? Please let me know where I can be clearer, maybe it's bco my English :)

moniuch avatar Mar 06 '22 08:03 moniuch

@moniuch Thanks for the clarification, I got confused with the comment because it seemed like you wanted to add reducers to the action reducer map.

I think that an additional schematic isn't required, what do you think if we would re-use the --reducers flag? Currently it only adds the new reducer to the feature, but we could extend it so it can also create the initial state interface and action reducer map.

timdeschryver avatar Mar 08 '22 18:03 timdeschryver

@timdeschryver Good we are on the same page now :) I will be glad to see any sort of solution, be it a new schematic or a new flag to the existing schematic that will just create a minimal reducer as in the first snippet. We can refer to it as --minimal or --bare

moniuch avatar Mar 08 '22 20:03 moniuch

@moniuch do you want to create a PR for this? To start simple I would not introduce any new flags and create an empty state/reducer where we currently do nothing (https://github.com/ngrx/platform/blob/master/modules/schematics/schematics-core/utility/ngrx-utils.ts#L89). If needed we can add that later.

timdeschryver avatar Mar 09 '22 18:03 timdeschryver

@timdeschryver Ekhm... WIth my zero experience in writing schematics this could take a while, but I'm gonna try anyway.

moniuch avatar Mar 10 '22 19:03 moniuch

Hi @moniuch,

Are you working on it? I can take this if its available.

santoshyadavdev avatar Mar 29 '22 19:03 santoshyadavdev

Hi @santoshyadavdev

I can take this if its available. Please do. Thank you.

moniuch avatar Mar 30 '22 18:03 moniuch

@timdeschryver can you assign this to me

santoshyadavdev avatar Mar 30 '22 19:03 santoshyadavdev

@santoshyadavdev are you working on it ? Otherwise I can take this PR

tomalaforge avatar Dec 22 '22 08:12 tomalaforge

You can take this one.

santoshyadavdev avatar Dec 22 '22 10:12 santoshyadavdev