I installed the react-native-really-awesome-button
package because it has some really awesome buttons.
Unfortunately, it seems like the onPress
handler does not work as intended.
Here is a reproducible demo: https://snack.expo.dev/@anshnanda/awesomebutton-not-working
You can clearly see that the onPress
function just uses the initial state and is not updated with new state.
It is clear that AwesomeButton is just using the initial state value of num
which leads me to think the function is wrapped with useMemo
or useCallback
(with a likely bug), however, digging through the source code, I couldn't find why this happens.
I tried using onPressIn
and onPressOut
and those seem to be working fine, except the UX is obviously worse since the user doesn't see the slick animation that I downloaded react-native-really-awesome-button
for.
CodePudding user response:
It seems like you are asking for confirmation that it's a bug/issue in the AwesomeButton
, and I agree that it is.
The onPress
callback is stored in a React ref: (source)
const debouncedPress = useRef( debouncedPressTime ? debounce( (animateProgressEnd: (callback?: any) => void) => onPress(animateProgressEnd), // <-- here debouncedPressTime, { trailing: false, leading: true, } ) : onPress // <-- here );
debouncedPress
is called in a press
handler: (source)
const press = () => { actioned.current = true; if (progressing.current === true) { return; } if (progress === true) { requestAnimationFrame(startProgress); } debouncedPress.current(animateProgressEnd); // <-- here };
press
is called by the handlePressOut
callback: (source)
It's the animateProgressEnd
handler that calls the passed onPress
function (debouncedPress) though: (source)
const animateProgressEnd = (callback: any) => { // <-- your callback if (progress !== true) { return; } if (timeout?.current) { clearTimeout(timeout.current); } requestAnimationFrame(() => { animateTiming({ variable: animatedLoading, toValue: 1, }).start(() => { Animated.parallel([ animateElastic({ variable: textOpacity, toValue: 1, }), animateElastic({ variable: activityOpacity, toValue: 0, }), animateTiming({ variable: loadingOpacity, toValue: 0, delay: 100, }), ]).start(() => { animateRelease(() => { progressing.current = false; callback && callback(); // <-- called here onProgressEnd && onProgressEnd(); }); }); }); }); };
So yes, the React ref debouncedPress
is never updated to hold a newer instance of your passed onPress
callback handler.
To get around this you can use a React ref in your code to cache the num
state and reference this in the printCurrNum
handler.
Example:
function App() {
const [num, setNum] = React.useState(0);
const [currentValue, setCurrentValue] = React.useState(0);
const numRef = React.useRef(num); // <-- ref to cache num state
React.useEffect(() => { // <-- effect to update cache
numRef.current = num;
}, [num]);
const increment = () => {
setNum((num) => num 1);
};
const printCurrNum = () => {
console.log(numRef.current); // <-- reference current value
setCurrentValue(numRef.current); // <-- reference current value
};
return (
<View style={styles.container}>
<Text>Current value according to AwesomeButton: {currentValue}</Text>
<Text>{num}</Text>
<AwesomeButton onPress={printCurrNum}>
Update current value with Awesome Button
</AwesomeButton>
<Button title="Increment" onPress={increment} />
<Button
title="Update current value with native button"
onPress={printCurrNum}
/>
</View>
);
}
https://snack.expo.dev/@drew.w.reese/awesomebutton-not-working