react-router-native-stack
react-router-native-stack copied to clipboard
"Double tap" causes the animation to run twice and double rendering
Some people have fast fingers and some have slower devices.
I have noticed that if i tap a navigation link twice (before the animation has started), the animation runs twice, rendering the next route twice and "double stacking" the views. Obviously this is an issue... but i should imagine it would be a reasonably easy fix with an isAnimating
hook to stop further requests after initial instruction?
Yes this is an issue. A fix could be to pass an isAnimating prop to the child components of the Route. I'd be open to any PRs implementing a fix!
A work around could be to make your links only tappable once, perhaps with some local state. Since in the current architecture of the Stack transitions triggered by a link aren't cancellable, and the stack will always unmount the previous route component and re-mount a new route component on every transition, that 'only tappable once' state would get re-set after every navigation action.
This issue also happen with react-navigation, I guess disable link before pushing is answer.
Yet a quick hotfix can be monkey patching history.push & pop and denounce them for 100ms & re enable after interactionManager signal end of animation
We created an Animation context that can inform touchable elements when the stack navigator is animating so as to block onPress
events for specific components:
// Create context
const AnimationContext = React.createContext({
stackNavIsAnimating: false,
handleNavIsAnimating: null,
})
export interface State {
stackNavIsAnimating: boolean
}
export class AnimationProvider extends React.Component<{}, State> {
state = {
stackNavIsAnimating: false,
}
public handleNavIsAnimating = (isAnimating: boolean): void => {
this.setState({ stackNavIsAnimating: isAnimating })
}
public render() {
const { stackNavIsAnimating } = this.state
return (
<AnimationContext.Provider
value={{
stackNavIsAnimating,
handleNavIsAnimating: this.handleNavIsAnimating,
}}
>
{this.props.children}
</AnimationContext.Provider>
)
}
}
// Capturing Stack navigation state
<AnimationContext.Consumer>
{({ handleNavIsAnimating, stackNavIsAnimating }) => {
return (
<Stack isAnimating={handleNavIsAnimating}>
)
}}
</AnimationContext.Consumer>
// Disable touchable element when stack nav is animating
<AnimationContext.Consumer>
{({ stackNavIsAnimating }) => (
<BackButton
onPress={stackNavIsAnimating ? noop : history.goBack}
/>
)}
</AnimationContext.Consumer>
or as a hook:
// Make hook
export const useAnimationContext = () => useContext(AnimationContext)
// Example usage
const { stackNavIsAnimating } = useAnimationContext()
<TouchableOpacity onPress={!stackNavIsAnimating ? () => goBack() : noop} />