I have a react component in react native that I want to handle hardwareBackButton manually. I have different behavior when a redux state is true or false in backHandler function that I pass to hardwareBackPressListener.
const brandSelected = useSelector(state => state.map.brandSelected);
I have this useSelector in my component to access the state. and I have useEffect function that I monitor the changes of this state: (that has correctly work and log the state when it changes to true or false.
React.useEffect(() => {
console.log(brandSelected); // this is false correctly
}, [brandSelected]);
and finally I have a backHandler function that I pass it to hardwareBackPress Listener.
React.useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', backHandler);
return () => {
BackHandler.removeEventListener('hardwareBackPress', backHandler);
};
}, []);
and backHandler function:
const backHandler = () => {
console.log('check, backhandler', brandSelected) // it logs true continuously
if (brandSelected === true) {
dispatch(
dispatchItemToRedux({
type: CATEGORIES_SELECTION,
payload: {
brandSelected: false,
},
}),
);
return true;
}
popScreen(Screens.Map);
return true;
};
But this function does not notify that the brandSelected state changed. the first time it works correctly and dispatch function and changes the redux state correctly and useEffect function log false correctly. but in other tries it does not work correctly and nothing changed!!
CodePudding user response:
The issue here is a stale enclosure of the brandSelected
in the backHandler
function you passed to the "hardwareBackPress"
event listener on the initial render cycle. backHandler
function only ever has the value from the initial render cycle and will never update/re-enclose an updated value.
To resolve you should cache the backHandler
state value in a React ref that you can reference in the callback handler.
const brandSelected = useSelector(state => state.map.brandSelected);
const brandSelectedRef = React.useRef(brandSelected);
useEffect(() => {
brandSelectedRef.current = brandSelected;
}, [brandSelected]);
...
const backHandler = () => {
console.log('check, backhandler', brandSelectedRef.current)
if (brandSelectedRef.current) {
dispatch(
dispatchItemToRedux({
type: CATEGORIES_SELECTION,
payload: {
brandSelected: false,
},
}),
);
return true;
}
popScreen(Screens.Map);
return true;
};
An alternative would be to move backHandler
into the useEffect
hook setting the event listeners and use the brandSelected
state as a dependency so the updated state value is re-enclosed in the callback.
React.useEffect(() => {
const backHandler = () => {
console.log('check, backhandler', brandSelected)
if (brandSelected) {
dispatch(
dispatchItemToRedux({
type: CATEGORIES_SELECTION,
payload: {
brandSelected: false,
},
}),
);
return true;
}
popScreen(Screens.Map);
return true;
};
BackHandler.addEventListener('hardwareBackPress', backHandler);
return () => {
BackHandler.removeEventListener('hardwareBackPress', backHandler);
};
}, [brandSelected]);