profile
viewpoint

Ask questionsPressable has unchangeable delay for opacity effect (onPressIn)

Description

The <Pressable /> component has an unchangeable delay between pressing and visual feedback.

See:

<table> <tr> <th>Pressable</th> <th>TouchableOpacity</th> </tr> <tr> <td><img src="https://user-images.githubusercontent.com/15199031/87524565-9b34c480-c688-11ea-809c-f47c3485bc64.gif" height=120/></td> <td><img src="https://user-images.githubusercontent.com/15199031/87524581-9ec84b80-c688-11ea-870f-89f7d2a78476.gif" height=120/></td> </tr> </table>

As you can see, the TouchableOpacity has instant visual feedback when I press it, where as the Pressable takes about 120ms (wild guess). I though https://github.com/facebook/react-native/commit/49b2a6e9cd95b74375f51fda87b87430073ec20e should fix this delay?

React Native version:

info Fetching system and libraries information...
(node:46144) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
System:
    OS: macOS 10.15.5
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 6.86 GB / 32.00 GB
    Shell: 5.7.1 - /bin/zsh
  Binaries:
    Node: 14.4.0 - /usr/local/bin/node
    Yarn: 1.22.4 - /usr/local/bin/yarn
    npm: 6.14.6 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  Managers:
    CocoaPods: 1.9.3 - /usr/local/bin/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 13.5, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
    Android SDK:
      API Levels: 25, 28, 29
      Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.0, 30.0.0, 30.0.0
      System Images: android-29 | Google APIs Intel x86 Atom, android-29 | Google Play Intel x86 Atom
      Android NDK: 21.2.6472646
  IDEs:
    Android Studio: 4.0 AI-193.6911.18.40.6514223
    Xcode: 11.5/11E608c - /usr/bin/xcodebuild
  Languages:
    Java: 1.8.0_252 - /usr/bin/javac
    Python: 2.7.16 - /usr/bin/python
  npmPackages:
    @react-native-community/cli: Not Found
    react: 16.13.1 => 16.13.1 
    react-native: 0.63.1 => 0.63.1 
  npmGlobalPackages:
    *react-native*: Not Found

Steps To Reproduce

Provide a detailed list of steps that reproduce the issue.

  1. Create <Pressable /> component
  2. Give it an opacity effect with the new style function that supplies the pressed argument
  3. See delay between press and effect

Expected Results

The opacity effect/visual feedback should appear instantly, like on the TouchableOpacity.

Snack, code example, screenshot, or link to a repository:

PressableOpacity.ts:

import React, { useCallback } from 'react';
import { Pressable, PressableProps, PressableStateCallbackType, StyleProp, ViewStyle } from 'react-native';

export interface PressableOpacityProps extends PressableProps {
	disabledOpacity?: number;
}

export type StyleType = (state: PressableStateCallbackType) => StyleProp<ViewStyle>;

export default function PressableOpacity(props: PressableOpacityProps): JSX.Element {
	const { style, disabled, disabledOpacity, ...passThroughProps } = props;

	const getOpacity = useCallback(
		(pressed: boolean) => {
			if (disabled) return disabledOpacity ?? 1;
			else return pressed ? 0.6 : 1;
		},
		[disabled, disabledOpacity],
	);
	const _style = useCallback<StyleType>(({ pressed }) => [style, { opacity: getOpacity(pressed) }], [getOpacity, style]);

	return <Pressable style={_style} disabled={disabled} {...passThroughProps} />;
}

Now just use it:

App.js

export default function App() {
  return (<PressableOpacity style={{ width: 200, height: 30 }} onPress={() => console.log('hello!')} />);
}
facebook/react-native

Answer questions yungsters

Thanks for the tag, @andreialecu.

I'd like to share some history, the purpose of the delay, what changed recently, and what I propose that we do next.

The default "press in" delay of 130ms was always in Touchable.Mixin, but it was accidentally always overridden to zero by TouchableWithoutFeedback, et al.

The purpose of this delay is to prevent accidental activations when a user initiates a scroll gesture. For example, if you have a <ScrollView> and a <Pressable> within it, we do not want the <Pressable>'s "pressed" style to be displayed if the user only happened to press it as the start of a scrolling / swiping gesture. The 130ms delay creates enough time for the <ScrollView> to emit a cancel event if a scroll gesture is detected.

Keep in mind that when a "press out" is detected within the 130ms, we immediately fire onPressIn and onPressOut. However, as mentioned above, we still have a problem in which a user who quickly taps an interactive element does not experience any visual feedback. This is because when onPressIn and onPressOut fire instantaneously, the onPressIn visual side effect is not visible. To fix this, we introduced a minimum press duration so that onPressOut will always fire at least 130ms after onPressIn (to give people time to see the visual feedback of the element being pressed).

The fix for preserving the default press in duration was only intended for <Pressable>. But when TouchableWithoutFeedback (et al) was refactored to use Pressability, I forgot to inherit the original bug. We can fix that.

There is still the remaining question of what the default behavior for <Pressable> should be. I recognize the concerns around unnecessary delays, but I also want to highlight the purpose of the delay (to avoid accidental activations on scroll gestures). We could certainly introduce a delayPressIn prop to <Pressable>, but I think we should avoid it if possible because I believe the only use case would be setting delayPressIn={0}.

One thing we could do is change <Pressable> to default to 130ms only when nested within a <ScrollView>. The downside of this approach is that if the scrolling container is rendered by a parent view (outside of React Native), <Pressable> may not know about that and will not behave correctly.

Curious to hear what everyone thinks. Thanks!

useful!

Related questions

Crash on email detection in TextInput on Xiaomi devices running android 10 hot 11
Cannot find module 'C:\Users\USER\AppData\Roaming\npm\node_modules\expo-cli\bin\expo.js hot 6
How to fix the error Value for title cannot be cast from ReadableNativeMap to String hot 6
xcode 11.4 build fatal error: module map file xxx/Build/Products/Debug-iphoneos/YogaKit/YogaKit.modulemap&#39; not found hot 6
ERR_INVALID_ARG_TYPE(name, 'string', value); ^ TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined at validateString (internal/validators.js:125:11) at Object.join (path.js:1147:7)
native_modules.gradle file is not downloading
Undefined symbol on Flipper using 0.62 on real iOS device
"RnDiffApp" has not been registered - 0.60.5 to 61.0-rc.2 hot 4
FlatList TypeError: undefined is not an object (evaluating 'props.getItem') hot 4
Error: Cannot find module 'warnOnce' hot 4
error Failed to install the app. Make sure you have the Android development environment set up: https://facebook.github.io/react-native/docs/getting-started.html#android-development-environment. Run CLI with --verbose flag for more details. Error: Command failed: gradlew.bat app:installDebug -PreactNativeDevServerPort=8081 :ReactNative:Unexpected empty result of running 'npx.cmd --quiet --no-install react-native config' command from 'null' directory. :ReactNative:Running 'npx.cmd --quiet --no-install react-native config' command from 'null' directory failed. hot 4
8 errors while debugging new react native app Execution failed for task ':app:compileDebugJavaWithJavac'. - React Native hot 4
Hi! I created new react native project and I can't build on android hot 4
react native 0.62 crash down in android 4.4.2 - react-native hot 3
React-native I18nManager.forceRTL doesn't applies changes in first app load hot 3
source:https://uonfu.com/
Github User Rank List