React导航防止双推()

wtzytmuj  于 2023-01-09  发布在  React
关注(0)|答案(3)|浏览(102)

我正在用react-navigation-4.2.1构建一个应用。这个应用有多个堆栈导航器。所以有很多navigation.push('Routename')调用。问题是当控制面(即TouchableOpacity)被快速点击多次(第一次,其余的在屏幕转换过程中)我最终将多个屏幕推送到堆栈中。是否有办法将表面限制为第一次点击/调用push()?

webghufk

webghufk1#

下面的组件是我用来让东西可以触摸的。它可以在很短的时间内处理多个触摸。

  • 使用下面的组件代替TouchableOpacity。用这个组件 Package 你想要的任何东西,它将是可触摸的。
<SafeTouch
    onPress={...}
>
    <Text> hey! im a touchable text now</Text>
</SafeTouch>
  • 下面的组件使用TypeScirpt编写。
  • 第一次触摸后300毫秒内的每一次触摸都会被忽略(这就是帮助你解决问题的地方)。
import * as React from 'react'
import { TouchableOpacity } from 'react-native'

interface ISafeTouchProps {
    onPress: () => void
    onLongPress?: () => void
    onPressIn?: () => void
    onPressOut?: () => void,
    activeOpacity?: number,
    disabled?: boolean,
    style: any
}

export class SafeTouch extends React.PureComponent<ISafeTouchProps> {
    public static defaultProps: ISafeTouchProps = {
        onPress: () => { },
        onLongPress: () => { },
        onPressIn: () => { },
        onPressOut: () => { },
        disabled: false,
        style: null
    }
    private isTouchValid: boolean = true
    private touchTimeout: any = null
    public constructor(props: ISafeTouchProps) {
        super(props)
        {// Binding methods
            this.onPressEvent = this.onPressEvent.bind(this)
        }
    }
    public render(): JSX.Element {
        return (
            <TouchableOpacity
                onPress={this.onPressEvent}
                onLongPress={this.props.onLongPress}
                onPressIn={this.props.onPressIn}
                onPressOut={this.props.onPressOut}
                activeOpacity={this.props.activeOpacity}
                disabled={this.props.disabled}
                style={[{minWidth: 24, minHeight: 24}, this.props.style]}
            >
                {
                    this.props.children
                }
            </TouchableOpacity>
        )
    }
    public componentWillUnmount() {
        this.clearTimeoutIfExists()
    }
    private onPressEvent(): void {
        requestAnimationFrame(() => {
            if (this.isTouchValid === false) {
                return
            }
            this.isTouchValid = false
            this.clearTimeoutIfExists()
            this.touchTimeout = setTimeout(() => {
                this.isTouchValid = true
            }, 300)
            if (typeof this.props.onPress === 'function') {
                this.props.onPress()
            }
        })
    }
    private clearTimeoutIfExists(): void {
        if (this.touchTimeout != null) {
            clearTimeout(this.touchTimeout)
            this.touchTimeout = null
        }
    }
}
khbbv19g

khbbv19g2#

这是Push的正确行为,如果您想避免双选项卡上的重复屏幕,可以使用navigation.navigate。

ldioqlga

ldioqlga3#

为了避免在短时间内点击同一个按钮时多次按下屏幕,我创建了一个通用钩子来避免多次运行一个函数(接受一个允许再次运行的间隔):

export const useCallOnce = <T extends unknown[], K>(
    fn: (...args: T) => K,
    allowAfter?: number,
) => {
    const ref = React.useRef<number | undefined>();
    const resultFn = (...args: T) => {
        const now = new Date().getTime();

        if (!ref.current || (allowAfter && ref.current + allowAfter < now)) {
            ref.current = now;
            return fn(...args);
        }
    };
    return resultFn;
};

然后,您可以像下面的示例那样调用它:

const navigation = useNavigation<NativeStackNavigationProp<{ ExampleScreen: undefined }>>();
const push = useCallOnce(() => navigation.push('ExampleScreen'), 500);
// just call on the button click event as: onSomeEvent={() => push()}

您可以创建一个通用按钮组件,它接受带有钩子的push参数(类似于上面的示例),并在希望按钮在页面之间导航时使用此按钮。

相关问题