Reputation: 6013
I'm working with the Redux Tool Kit in a TypeScript project. One of the things the Tool Kit allows is using mutations inside reducers, although one is not directly mutating the state -- when one is using their createReducer
call:
const todosReducer = createReducer([], {
ADD_TODO: (state, action) => {
// "mutate" the array by calling push()
state.push(action.payload)
},
TOGGLE_TODO: (state, action) => {
const todo = state[action.payload.index]
// "mutate" the object by overwriting a field
todo.completed = !todo.completed
},
REMOVE_TODO: (state, action) => {
// Can still return an immutably-updated value if we want to
return state.filter((todo, i) => i !== action.payload.index)
}
})
I've defined my state like this:
type InitialArticlesState = {
articles: Articles[];
currentArticle: Article | undefined;
tags: TagList;
currentTags: TagList;
currentArticleNumber: number;
totalArticlesNumber: number;
currentSearchString: string;
error: string;
};
const initialState: InitialArticlesState = {
articles: [],
currentArticle: undefined,
tags: [],
currentTags: [],
currentArticleNumber: 0,
totalArticlesNumber: 0,
currentSearchString: '',
error: ''
};
TypeScript now complains about state.push
here:
[articlesReceived.type]: (state, action: PayloadAction<Articles[]>) => {
state.push(action.payload);
},
saying
What is the best way to solve this? I'm a relative TS newbie, so it's not clear to me how to start.
It would seem that when the app arrives at the reducer, state is a proxy for something else. So I'm not sure that types make sense in this context -- what is defined in the code is not what arrives at runtime...or is there a way to do it?
Upvotes: 1
Views: 1357
Reputation: 67499
It looks like there's a few different issues here.
First, your reducer function does not match the state type you've defined code-wise. Your initialState
value is an object, not an array. But, your code is acting like state
is an array by calling state.push()
, which is why TypeScript is giving you the error telling you that's not possible.
Second, your payload
is typed as an Article[]
, but you're calling .push()
somewhere. Did you actually want to assign that array into the state and overwrite any existing articles? If so, what you actually want is:
state.articles = action.payload;
Third, by calling createReducer
and passing in an object of reducers, you're going to have a harder time trying to type the state and action correctly. Per our Usage with TypeScript docs page, you should use the "builder callback" form of createReducer
to get better typing behavior:
createReducer(initialState, builder => {
builder.addCase(typedActionCreator, (state, action) => {
// state and action are both correctly inferred here
});
})
And finally, most of the time you shouldn't be calling createReducer
by hand anyway. We recommend using createSlice
for most of your logic, which will auto-generate the action creators for you.
Upvotes: 1