I am new. to NgRx.
When I am trying to create reducer using createReducer()
I am getting error Expected 4 arguments, but got 2.
When I am trying to pass 3rd and 4th argument as null, I am getting error
Argument of type '(state: any, { updatedValue }: any) => any' is not assignable to parameter of type 'readonly ActionCreator<string, FunctionWithParametersType<any[], object>>[]'.
Type '(state: any, { updatedValue }: any) => any' is missing the following properties from type 'readonly ActionCreator<string, FunctionWithParametersType<any[], object>>[]': concat, join, slice, indexOf, and 15 more.ts(2345)
Reducer code
import { createReducer, on, State, Action } from '@ngrx/store';
import { Ingredient } from '../../shared/ingredient.model';
import * as ShoppingListAction from './shopping-list.action';
export const initialState = {
ingredients: [
new Ingredient('Apples', 5),
new Ingredient('Tomatoes', 10),
]
}
export const shoppingListReducer = createReducer(
initialState,
on(
'ADD_INGREDIENT',
(state: any, { updatedValue }: any) => ({ ...state, prop: updatedValue }),
null,
null
)
);
actions code
import { createAction, props } from '@ngrx/store';
import { Ingredient } from '../../shared/ingredient.model';
export const ADD_INGREDIENT = 'ADD_INGREDIENT';
export const AddIngredient = createAction(
ADD_INGREDIENT,
props<Ingredient>()
);
app.module.ts
import { SharedModule } from './shared/shared.module';
import { CoreModule } from './core.module';
import { LoggingService } from './logging.service';
// Store
import { shoppingListReducer } from './shopping-list/store/shopping-list.reducer';
@NgModule({
declarations: [AppComponent, HeaderComponent],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
SharedModule,
CoreModule,
StoreModule.forRoot({ 'slReduce': shoppingListReducer })
],
bootstrap: [AppComponent],
// providers: [LoggingService]
})
export class AppModule {}
package.json
"dependencies": {
"@angular/animations": "^11.2.1",
"@angular/mon": "^11.2.1",
"@angular/piler": "^11.2.1",
"@angular/core": "^11.2.1",
"@angular/forms": "^11.2.1",
"@angular/platform-browser": "^11.2.1",
"@angular/platform-browser-dynamic": "^11.2.1",
"@angular/router": "^11.2.1",
"@ngrx/store": "^11.0.1",
"bootstrap": "3.3.7",
"core-js": "^3.1.2",
"rxjs": "^6.0.0",
"tslib": "^2.0.0",
"zone.js": "~0.11.3"
}
I am new. to NgRx.
When I am trying to create reducer using createReducer()
I am getting error Expected 4 arguments, but got 2.
When I am trying to pass 3rd and 4th argument as null, I am getting error
Argument of type '(state: any, { updatedValue }: any) => any' is not assignable to parameter of type 'readonly ActionCreator<string, FunctionWithParametersType<any[], object>>[]'.
Type '(state: any, { updatedValue }: any) => any' is missing the following properties from type 'readonly ActionCreator<string, FunctionWithParametersType<any[], object>>[]': concat, join, slice, indexOf, and 15 more.ts(2345)
Reducer code
import { createReducer, on, State, Action } from '@ngrx/store';
import { Ingredient } from '../../shared/ingredient.model';
import * as ShoppingListAction from './shopping-list.action';
export const initialState = {
ingredients: [
new Ingredient('Apples', 5),
new Ingredient('Tomatoes', 10),
]
}
export const shoppingListReducer = createReducer(
initialState,
on(
'ADD_INGREDIENT',
(state: any, { updatedValue }: any) => ({ ...state, prop: updatedValue }),
null,
null
)
);
actions code
import { createAction, props } from '@ngrx/store';
import { Ingredient } from '../../shared/ingredient.model';
export const ADD_INGREDIENT = 'ADD_INGREDIENT';
export const AddIngredient = createAction(
ADD_INGREDIENT,
props<Ingredient>()
);
app.module.ts
import { SharedModule } from './shared/shared.module';
import { CoreModule } from './core.module';
import { LoggingService } from './logging.service';
// Store
import { shoppingListReducer } from './shopping-list/store/shopping-list.reducer';
@NgModule({
declarations: [AppComponent, HeaderComponent],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
SharedModule,
CoreModule,
StoreModule.forRoot({ 'slReduce': shoppingListReducer })
],
bootstrap: [AppComponent],
// providers: [LoggingService]
})
export class AppModule {}
package.json
"dependencies": {
"@angular/animations": "^11.2.1",
"@angular/mon": "^11.2.1",
"@angular/piler": "^11.2.1",
"@angular/core": "^11.2.1",
"@angular/forms": "^11.2.1",
"@angular/platform-browser": "^11.2.1",
"@angular/platform-browser-dynamic": "^11.2.1",
"@angular/router": "^11.2.1",
"@ngrx/store": "^11.0.1",
"bootstrap": "3.3.7",
"core-js": "^3.1.2",
"rxjs": "^6.0.0",
"tslib": "^2.0.0",
"zone.js": "~0.11.3"
}
I was seeing this error in my IDE, along with some other strange type-related errors with @ngrx/store, but the projects were piling just fine.
I had upgraded my typescript version in my projects that use @ngrx to the latest (v4.3.4 at the time), but my workspace included several other projects, and my IDE (I'm using WebStorm) typescript language service was configured to use an older version of typescript that was installed on one of my other projects. After configuring the IDE typescript service to use the newer typescript version, the errors went away.
So in my case, it was a couple different things - first, I needed to upgrade the typescript version for my projects that use @ngrx to work with some newer language features @ngrx is using. Then, I also had to make sure my IDE's typescript language service was using that version, or at least a fairly recent version of typescript to avoid seeing bogus errors in the editor windows.
Your action and your reducer look strange. According to the docs it should be something like:
export interface MyState {
ingredients: Array<Ingredient>;
}
export const initialState: MyState = {
ingredients: [
new Ingredient('Apples', 5),
new Ingredient('Tomatoes', 10),
]
}
// see https://ngrx.io/guide/store/actions
// note: the prop has a 'name: Type', not just 'Type'
export const AddIngredient = createAction(
ADD_INGREDIENT,
props<{ingredient: Ingredient}>()
);
// verbose func to show difference
export const shoppingListReducer = createReducer(
initialState,
on(
AddIngredient, // the action, not the action.type
(state, { ingredient }) => {
let ingredients = Array.from(state.ingredients);
ingredients.push(ingredient);
return { ingredients };
}
)
);
Or if you prefer the shorthand:
// equal shorthand function
export const shoppingListReducer = createReducer(
initialState,
on(
AddIngredient, // the action, not the action.type
(state, { ingredient }) => ({ingredients: [...state.ingredients, ingredient]})
)
);
The main points are:
(state: any, { updatedValue }: any) => ({ ...state, prop: updatedValue })
(*) If this worked, it would lead to a state like
interface MyAccidentalState {
ingredients: Array<Ingredient>;
prop: Ingredient;
}
... but it does not work, as your action has no named prop 'updatedValue' and prop: updatedValue
was not {prop: updatedValue}
.
And because it has no named prop 'updatedValue', the piler explodes at the object destructuring part of
(state: any, { updatedValue }: any) => ...
And the any-typing in (state: any, { updatedValue }: any) => ...
may deny the piler some knowledge which was already there: updatedValue was intended to be an Ingredient, and state is (now) a MyState.
Side-note:
Keeping the state of a collection is something where ngrx/entity might be useful - but your way is valid, too.
Make sure you don't have a missmatch in the @ngrx/store library version and your installed @angular/core version.
Normally they should be on the same major version - for @angular/core ^9.x.x you should use a respective ^9.x.x version of @ngrx/store. This happened to me when I was on Angular 9 and installed the latest, stable ngrx version (which currently is 11.x.x).
Read more about the requirements (link refers to V9, but can apply for newer ones aswell): https://ngrx.io/guide/migration/v9