I added a context that contains a useReducer
hook to my Ionic React app. I'm seeing some strange behavior: when I update the context value with a dispatch
call, then a consumer component will be updated on the page, but the exact same component on the tab bar does not get updated.
I followed this tutorial.
When I add console.log
statements to check whether the components are being reloaded, I see that the component placed in the tab bar (<TabBarCounter>
) is not being reloaded even though the context value has changed.
When I add console.log
statement to check for re-rendering in my context provider, I see that it doesn't get re-rendered when a dispatch
is called, either.
It seems like the context is being updated locally rather than globally. There is a comment in this answer:
You are updating your state correctly using a reducer but it will only update local component state not the global context state.
That sounds a lot like the problem I am having here.
Here's some code:
MyContext.tsx
export const CountContext = React.createContext<any>({} as {
countState: CountState,
countDispatch: React.Dispatch<CountReducerActions>,
});
interface MyProps {
children: JSX.Element,
}
const reducer = (countState: CountState, action: CountReducerActions) => {
switch (action.type) {
case 'add1': {
countObject.total += 1;
return countObject;
}
default: {
throw new Error();
}
}
};
export const CountContextProvider: React.VFC<MyProps> = ({ children }: MyProps) => {
const [countState, countDispatch] = useReducer(
reducer,
{
total: 0,
},
);
return (
<CountContext.Provider value={{ countState, countDispatch }}>
{children}
</CountContext.Provider>
);
};
how I update the context
const { countState, countDispatch } = useContext(CountContext);
countDispatch({ type: 'add1' });
MyComponentThatDoesNotGetRerendered.tsx
import React, { useContext } from 'react';
import { IonBadge } from '@ionic/react';
import { CountContext } from '../../context/CountContext';
const TabBarCounter: React.VFC = () => {
const [countState] = useContext(CountContext);
return (
<IonBadge>
{countState.total}
</IonBadge>
);
};
export default TabBarCounter;
Router.tsx
<CountContextProvider>
<IonReactRouter>
<AppTabBar>
<IonRouterOutlet>
<Route exact path={myPageRoute}>
<MyPage />
</Route>
<Route>
<PageError404 />
</Route>
</IonRouterOutlet>
</AppTabBar>
</IonReactRouter>
</CountContextProvider>
AppTabBar.tsx
const AppTabBar: React.VFC<MyProps> = ({ children }: MyProps) => {
const [userObject] = useContext(UserContext);
return (
<IonTabs>
{children}
<IonTabBar slot="bottom" id="appTabBar">
<IonTabButton tab="tab-settings" href={routeTabSettings}>
<IonLabel>Settings</IonLabel>
</IonTabButton>
<IonTabButton
tab="tab-count"
href={routeTabCount}
>
<TabBarReviewCounter />
<IonLabel>Count</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
};
In this case, when the context is updated in <MyPage>
, the <TabBarCounter>
that is inside <AppTabBar>
does not get updated, but the <TabBarCounter>
inside <MyPage>
does get updated.
How do I update the context correctly using useReducer()
so that when I update the context value, all the consumers of that context get updated?