I have an odd situation where a state variable is updating successfully in one component but not in the other. I'm successfully changing the state
, because I can see it reflected in the component where I'm triggering a dispatch
. But in another component that is wrapped in the same Provider
, I see no changes.
Other Stack Overflow answers seem to mostly recommend not directly mutating state, but here I'm not doing that - I'm dispatching an action to update the state, similar to what you'd see in Redux syntax.
TeaSettingsContext.tsx
const TeaSettingsContext = createContext<{ state: State; dispatch: Dispatch } | undefined>(undefined);
const teaSettingsReducer = (state: State, action: Action) => {
switch (action.type) {
case TeaSettingsActions.ChooseTea: {
return { ...state, chosenTea: action.payload };
}
case TeaSettingsActions.ChangeStrength: {
return { ...state, desiredStrength: action.payload };
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
};
export function TeaSettingsProvider({
children,
}: TeaSettingsProviderProps): Context {
const [state, dispatch] = useReducer(teaSettingsReducer, {
chosenTea: {},
desiredStrength: 0.5,
});
return (
<TeaSettingsContext.Provider value={{ state, dispatch }}>
{children}
</TeaSettingsContext.Provider>
);
}
export const useTeaSettingsContext = (): TeaSettingsContext => {
const context = useContext(TeaSettingsContext);
if (context === undefined) {
return;
}
return context;
};
Here's the "successful" component:
Home.tsx
<TeaSettingsProvider>
<StrengthSlider />
</TeaSettingsProvider>
StrengthSlider.tsx
export default function StrengthSlider(): ReactNode {
const { state, dispatch } = useTeaSettingsContext();
console.log(state.desiredStrength) // logs the increment on each press.
const incrementStrength = () => {
dispatch({
payload: state.desiredStrength + 0.1,
type: "change-strength",
});
};
return (
<Box position="relative" w="100%">
<Button title="Press" onPress={incrementStrength} />
<Text>{state.desiredStrength}</Text>
</Box>
);
}
...and the unsuccessful re-render happens in this component:
TeaPage.tsx
const Component = () => {
if (type === "tea") {
return data.map((teaObj) => (
<TeaCard id={teaObj.id} teaData={teaObj.data} key={teaObj.id} />
));
}
};
return (
<TeaSettingsProvider>
<Component />
</TeaSettingsProvider>
);
TeaCard.tsx
export function TeaCard({ id, teaData }: TeaCardProps): ReactNode {
const { state, dispatch } = useTeaSettingsContext();
console.log(state.desiredStrength); // this always logs the starting value of 0.5 and doesn't log each time I press the button above.
// ...
}
FYI: Based my code on Kent C Dodds' article: https://kentcdodds.com/blog/how-to-use-react-context-effectively