react-native-windows icon indicating copy to clipboard operation
react-native-windows copied to clipboard

Cannot Mask <TextInput> to Pattern Without Flickering

Open jbcullis opened this issue 2 years ago • 12 comments

Problem Description

Using setNativeProps({text: 'xxx'}) on a TextInput the text is not updated.

Steps To Reproduce

Create following element:

<TextInput ref={ele => this.Element = ele} defaultValue={null} onChangeText={(e) => { this.Element.setNativeProps({text: null}); }}></TextInput>

Expected Results

Element should null out the field after each change.

CLI version

npx react-native --version

Environment

OS: Windows 10 10.0.22000 CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz Memory: 21.85 GB / 31.73 GB Binaries: Node: 16.13.0 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.17 - ~\AppData\Roaming\npm\yarn.CMD npm: 8.1.3 - C:\Program Files\nodejs\npm.CMD Watchman: Not Found SDKs: Android SDK: Not Found Windows SDK: AllowDevelopmentWithoutDevLicense: Enabled AllowAllTrustedApps: Enabled Versions: 10.0.18362.0, 10.0.19041.0, 10.0.22000.0 IDEs: Android Studio: Not Found Visual Studio: 17.0.31912.275 (Visual Studio Community 2022) Languages: Java: Not Found npmPackages: @react-native-community/cli: Not Found react: 17.0.2 => 17.0.2 react-native: 0.66.0 => 0.66.0 react-native-windows: 0.66.5 => 0.66.5 npmGlobalPackages: react-native: Not Found

npx react-native info

Target Platform Version

No response

Target Device(s)

No response

Visual Studio Version

No response

Build Configuration

No response

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

No response

jbcullis avatar Feb 26 '22 16:02 jbcullis

setNativeProps is pretty internal, and also going away with Fabric. What is the goal here, that cannot be accomplished using the public TextInput API?

NickGerleman avatar Feb 27 '22 06:02 NickGerleman

@NickGerleman It's a nice tool to have when building masked textboxes however I didn't realize it was on the way out so that would make sense to not implement it.

Specifically, we have a masked input control and trying to work around the nuance of rn-macos vs rn-windows. For example, rn-macos hasn't implemented casing yet so we were using toUpper as a workaround. Then we ran into the issue where cursor position was being lost when an invalid key was entered. We were able to get around these issues on mac by using a combo of defaultValue and setNativeProps however that didn't work on windows because setNativeProps doesn't seem to be implemented.

Ultimately we ended up creating a fix for the cursor position which you can find in #9615 and my intention is to make the same changes to macos tomorrow.

I've been thinking about it today and what we really need is the ability to define the data format and have the keydownpreview reject keys before it even makes it to the javascript thread...of course this needs to be considered upstream since it would apply to all platforms. Currently there is a widely acknowledge drawback to manipulating onChangeText which is you get a flicker as the characters make the round trip, preventing invalid keys at keydownpreview would solve that entirely.

jbcullis avatar Feb 27 '22 06:02 jbcullis

Currently there is a widely acknowledge drawback to manipulating onChangeText which is you get a flicker as the characters make the round trip.

Yeah, it does seem like an inherent limitation, since any text will be entered in the control on the UI thread, before the JS thread or React is notified of the change. It seems like the manipulation would need to happen on the UI thread, as the mutation happens there. I like the idea you mentioned of adding data formats to TextInput, for it to do the masking itself. Something more immediately actionable for single use-cases would be to implement your own View Manager, that handles the TextInput logic on the native side.

For more detail on setNativeProps, the API is mostly meant for internal components, to update the shadow node out of sync with the React component. Fabric changes a bit of how this works, so that the ShadowNode always looks like the native component properties of the React Tree. The names of native properties, etc, is implementation dependent. Guessing this is a trick that folks are using for Android/iOS right now?

NickGerleman avatar Feb 27 '22 06:02 NickGerleman

@NickGerleman I hadn't used it in a while, back in the early days of react, fast typing would cause it to drop characters so we used to use it to reduce errors.

If we could set basic types like date, int, dec1, dec2, dec3, dec4, str, that would probably cover the vast majority of masked input cases for line of business apps which I suspect is the majority of rn-windows. There is also the validkeysdown prop that I've seen on a few threads in macos. I did give it a shot but it didn't restrict keys on windows.

jbcullis avatar Feb 27 '22 06:02 jbcullis

FYI @Saadnajmi in case you have any thoughts here, re the react-native-macos prop.

I wonder if there is a precedent in popular APIs for how these sorts of declarative "allowed text patterns" look. I do agree it is useful, and these APIs become even more useful when supported across multiple platforms.

NickGerleman avatar Feb 27 '22 06:02 NickGerleman

@NickGerleman I hadn't used it in a while, back in the early days of react, fast typing would cause it to drop characters so we used to use it to reduce errors.

If we could set basic types like date, int, dec1, dec2, dec3, dec4, str, that would probably cover the vast majority of masked input cases for line of business apps which I suspect is the majority of rn-windows. There is also the validkeysdown prop that I've seen on a few threads in macos. I did give it a shot but it didn't restrict keys on windows.

ValidKeysDown is a macOS only prop. It also doesn't apply to TextInput, only View / Pressable. The closest equivalent on windows is 'keyDownEvents'. It specifies which keyboard events should only be handled in JS, and not natively. You can also specify whether to capture the event at the bubbling or capturimg phase. I'm not sure it works with textinput though.

Take a look at here for more info. https://github.com/microsoft/react-native-windows/blob/main/vnext/proposals/active/keyboard-reconcile-desktop.md#declarative-properties

Saadnajmi avatar Feb 27 '22 10:02 Saadnajmi

In RN Tester, there is a "live rewrite" example usage of TextInput. Every space you type is replaced with underscore. And at least for macOS, a lot of the TextInput behavior didn't work till 0.66. you might want to give those a look.

Saadnajmi avatar Feb 27 '22 10:02 Saadnajmi

Thanks @NickGerleman @Saadnajmi - unifying the api across mac and windows would be great.

I just tested onKeyDownCapture and I can see the event on windows (0.66) and can see the event but not cancel it. On mac (0.64) I cannot see the event. onKeyDownCapture={(e) => { console.log(e.nativeEvent); e.stopPropagation(); e.preventDefault(); }}

If this callback could cancel the event then we could move the masking logic here rather than onChangedText however there may be timing issues in doing this. Having the data type logic at the native thread would probably produce a more reliable result.

jbcullis avatar Feb 27 '22 13:02 jbcullis

@jbcullis was Saad's suggestion above about looking that "live rewrite" example helpful? You're on 0.64 for macOS I take it - is upgrading to 0.66 an option?

chrisglein avatar Feb 28 '22 19:02 chrisglein

@chrisglein i got into it a little bit yesterday, but got dragged into some support. I can see changes to the text input component on .66 but docs for macOS still list 64 as the stable version. I'll have a go at building for .66 later this week and see what happens. We are on .67 for windows. Ultimately, I think we need to prevent the round trip on masked input fields which means native support for either regex or data types are probably best suited.

jbcullis avatar Feb 28 '22 19:02 jbcullis

I think this should be changed to a feature request since the flickering issue is prevalent on all platforms.

jbcullis avatar Feb 28 '22 19:02 jbcullis

Hey, having access to programmatically set the TextInput value in the UI thread would greatly benefit more customized TextInputs with a native feel! I'm having the same use case as OP.

bombillazo avatar Aug 05 '22 13:08 bombillazo