I have a react hooks application written in typescript that contains multiple reducers, and I am using them with the context API.
I need to share the error state between reducers, because I need only one error state displayed in the app, which can be cleared/set with the errorReducer.
The trouble is, I need to set the error part of the state from the 'other' reducers (by 'other' I mean not the errorReducer).
If I try to use the useReducer hook (or my own useAsyncReducer hook) inside the 'other' reducers to set an error, I get
Error: Invalid hook call. Hooks can only be called inside of the body of a function component
, as you can see below.
How can I share state between reducers in react? (please see the 'TODO: THIS IS WHAT I NEED' below).
Please note that I do not want to use redux.
export type Actor = {
_id?: string,
firstName?: string,
lastName?: string,
selectedForDelete?: boolean
}
// Compound state for 3 reducers (activeEntityTypeReducer, actorReducer, errorReducer)
export type State = {
activeEntityType: string,
actors: Actor[],
error: string | null
}
export const EMPTY_INITIAL_STATE: State = {
activeEntityType: EntityType.ACTOR,
actors: [],
error: null
};
// ERROR REDUCER:
export const errorReducer = async (state: string | null, action: ErrorActions) => {
switch (action.type) {
case ErrorActionType.SET_ERROR: {
return action.payload;
}
default:
return state;
}
};
// ACTOR REDUCER:
export const actorReducer = async (state: Actor[], action: ActorActions) => {
// I cannot use here a hook like this because it triggers: "Error: Invalid hook call. Hooks can only be called inside of the body of a function component"
// const { dispatch: dispatchError } = useAsyncReducer(errorReducer, EMPTY_INITIAL_STATE);
switch (action.type) {
//... other actions
case ActorActionType.SEARCH_ACTORS: {
return fetch(
"http://localhost:3000/api/actors?pagesize=100"
).then(response => response.json())
.then(response => response['data']);
/* // TODO: THIS IS WHAT I NEED: continue the above line with a catch inside which I dispatch the error
.catch((error) => dispatchError({
type: ErrorActionType.SET_ERROR,
payload: error
}))
*/
}
default:
return state;
}
};
// MAIN (COMPOSED) REDUCER:
export const mainReducer = async ({ activeEntityType, actors, error }: State,
action: ActiveEntityTypeActions | ActorActions | ErrorActions) => (
{
actors: await actorReducer(actors, action as ActorActions),
activeEntityType: activeEntityTypeReducer(activeEntityType, action as ActiveEntityTypeActions),
// more reducers here and all need to set the error
error: await errorReducer(error, action as ErrorActions)
});