skip to Main Content

I have a simple text input field that accepts only numbers as the input. The onChange handler checks to see that the provided value is valid and then sets the state. If, however, validation fails, then it does nothing.

Everything works as expected, however the behavior seems strange to me. If validation fails, I still see the incorrectly provided value for a moment. It then goes away, but I feel like there’s something wrong here.

What I would like to do is replicate the behavior that the maxLength prop provides. When set to an integer, it does absolutely nothing (as is expected and as it should) if the provided value’s length is larger than the max length. However, in my current case, I’d like to validate the input first and make sure that the value is all numbers.

Perhaps there are some native modules that I can make changes to? How can I get this to work?

Here’s how it looks at the moment:

img1

Here’s the code:

import * as React from 'react';
import { TextInput } from 'react-native';

const MAX_LENGTH = 6;
const MyComponent = React.memo(() => {
    const [value, setValue] = React.useState('');

    const handleChange = React.useCallback((e) => {
        const isValidInput = (!e.trim() || !!parseInt(e)) && !/[a-z]/gi.test(e);

        if (isValidInput && e.length <= MAX_LENGTH) {
            setValue(e);
        }
    }, []);

    return (
        <TextInput
            value={value}
            onChange={handleChange}
            placeholder='Type only numbers...'
        />
    );
});

From the React-Native docs regarding maxLength:

Limits the maximum number of characters that can be entered. Use this instead of implementing the logic in JS to avoid flicker

And so this leaves me questioning if what I’d like to do is possible.

There’s also this recent comment on GitHub by the maintainer of the TextInput component. It seems as though this issue hasn’t been fixed thus far.

2

Answers


  1. To minimise flickering and only accept a certain number of numeric characters, something like this would work:

    <TextInput
      maxLength={MAX_LENGTH}
      keyboardType="numeric"
      value={value}
      onChangeText={(e) => {
        if (/^d+$/.test(e)) setValue(e);
      }}
      placeholder="Type only numbers..."
    />
    
    Login or Signup to reply.
  2. You could use onKeyPress and setNativeProps to reset the Input back to value quicker. onKeyPress fires before onChange but only delivers one char.

    Users won’t see any flicker except for lowEnd devices or first char.

      const [value, setValue] = React.useState("");
      const inputRef = React.useRef();
    
      const handleChange = (text) => {
        //... change value if should
      };
    
      const onKeyPress = () => {
        inputRef.current.setNativeProps({ text: value });
      };
    
      return (
        <TextInput
          ref={inputRef}
          value={value}
          onChangeText={handleChange}
          placeholder="Type only numbers..."      
          onKeyPress={onKeyPress}      
        />
      );
    

    For absolute zero flicker, you could

    • add a Text after InputText and wrap both inside a View.
    • Pass the same value props to both.
    • The Text would be below TextInput with absolute position and same fontSize.
    • Set TextInput color to ‘transparent’ and set caretHidden to true.

    Still, If you need to show a caret you can show a custom one next to Text.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search