175

I need to have a React Native TextInput component that will only allow numeric characters (0 - 9) to be entered. I can set the keyboardType to numeric which almost gets me there for input except for the period (.). However this does nothing to stop pasting non-numeric characters into the field.

What I've come up with so far is to use the OnChangeText event to look at the text entered. I remove any non-numeric characters from the text. Then put the text in a state field. Then update the TextInput through it's Value property. Code snippet below.

<TextInput 
  style={styles.textInput}
  keyboardType = 'numeric'
  onChangeText = {(text)=> this.onChanged(text)}
  value = {this.state.myNumber}
/> 

onTextChanged(text) {
  // code to remove non-numeric characters from text
  this.setState({myNumber: text})
}

This seems to work but it seems like a hack. Is there another way to do this?

Jose Vf
  • 1,493
  • 17
  • 26
Terry
  • 1,889
  • 2
  • 12
  • 8
  • Never got this example to work. Do you managed to solve it any other way? – amb Jan 05 '16 at 14:47
  • See also: https://stackoverflow.com/questions/40630918/react-native-input-component-takes-ony-numeric-values – hippietrail Nov 01 '17 at 13:17
  • Note that as of React Native 0.54, most of the answers suggested here are broken: https://github.com/facebook/react-native/issues/18874 (up to at least 0.56, which is the newest version at the time of writing). – Tomty Jul 25 '18 at 18:24
  • 1
    @sumitkumarpradhan That blog post suggests setting the keyboard type to 'numeric' which doesn't actually prevent text input. You can copy paste anything you want into there. – jmathew Jul 11 '19 at 15:42
  • 1
    I am using `keyboardType='numeric'` prop in TextInput to only show Numeric Keyboard (duh) and also replacing texts with regex `text.replace(/[^0-9]/g, '')` as suggested below to prevent anyone from pasting strings inside the TextInput. Is working fine so far on React Native v0.62 – KeshavDulal Sep 26 '20 at 07:06

24 Answers24

167

Using a RegExp to replace any non digit is faster than using a for loop with a whitelist, like other answers do.

Use this for your onTextChange handler:

onChanged (text) {
    this.setState({
        mobile: text.replace(/[^0-9]/g, ''),
    });
}

Performance test here: https://jsperf.com/removing-non-digit-characters-from-a-string

Jake Taylor
  • 2,412
  • 2
  • 13
  • 16
134

You can do it like this. It will only accept numeric values, and limit to 10 numbers as your wish.

<TextInput 
   style={styles.textInput}
   keyboardType='numeric'
   onChangeText={(text)=> this.onChanged(text)}
   value={this.state.myNumber}
   maxLength={10}  //setting limit of input
/>

You can see the entered value by writing the following code in your page:

{this.state.myNumber}
 

In the onChanged() function the code look like this:

onChanged(text){
    let newText = '';
    let numbers = '0123456789';
  
    for (var i=0; i < text.length; i++) {
        if(numbers.indexOf(text[i]) > -1 ) {
            newText = newText + text[i];
        }
        else {
            // your call back function
            alert("please enter numbers only");
        }
    }
    this.setState({ myNumber: newText });
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Venkatesh Somu
  • 4,710
  • 4
  • 23
  • 22
37

That is the correct way to do it till such a component (or attribute on the TextInput) is specifically developed.

The web has the ‘number’ type for the input element, but that is web based and react-native does not use a web view.

You could consider to create that input as a react component on it’s own (maybe call NumberInput): that’ll enable you to reuse it or maybe even open source it since you can create many TextInputs that has different value filters/checkers.

The downside to immediate correction is to ensure correct feedback is given to the user as to prevent confusion as to what happened to his value

Tjorriemorrie
  • 16,818
  • 20
  • 89
  • 131
33

React Native TextInput provides keyboardType props with following possible values : default number-pad decimal-pad numeric email-address phone-pad

so for your case you can use keyboardType='number-pad' for accepting only numbers. This doesn't include '.'

so,

<TextInput 
  style={styles.textInput}
  keyboardType = 'number-pad'
  onChangeText = {(text)=> this.onChanged(text)}
  value = {this.state.myNumber}
/>

is what you have to use in your case.

for more details please refer the official doc link for TextInput : https://facebook.github.io/react-native/docs/textinput#keyboardtype

Ganesh Krishna
  • 714
  • 1
  • 7
  • 13
30

First Solution

You can use keyboardType = 'numeric' for numeric keyboard.

  <View style={styles.container}>
        <Text style={styles.textStyle}>Enter Number</Text>
        <TextInput
          placeholder={'Enter number here'}
          style={styles.paragraph}
          keyboardType="numeric"
          onChangeText={value => this.onTextChanged(value)}
          value={this.state.number}
        />
      </View>

In first case punctuation marks are included ex:- . and -

Second Solution

Use regular expression to remove punctuation marks.

 onTextChanged(value) {
    // code to remove non-numeric characters from text
    this.setState({ number: value.replace(/[- #*;,.<>\{\}\[\]\\\/]/gi, '') });
  }

Please check snack link

https://snack.expo.dev/@vishaldhanotiya/numeric-keyboard

Vishal Dhanotiya
  • 2,512
  • 1
  • 13
  • 32
13

Only allow numbers using a regular expression

<TextInput 
  keyboardType = 'numeric'
  onChangeText = {(e)=> this.onTextChanged(e)}
  value = {this.state.myNumber}
/> 

onTextChanged(e) {
  if (/^\d+$/.test(e.toString())) { 
    this.setState({ myNumber: e });
  }
}

You might want to have more than one validation


<TextInput 
  keyboardType = 'numeric'
  onChangeText = {(e)=> this.validations(e)}
  value = {this.state.myNumber}
/> 

numbersOnly(e) {
    return /^\d+$/.test(e.toString()) ? true : false
}

notZero(e) {
    return /0/.test(parseInt(e)) ? false : true
}

validations(e) {
    return this.notZero(e) && this.numbersOnly(e)
        ? this.setState({ numColumns: parseInt(e) })
        : false
}

jasonleonhard
  • 12,047
  • 89
  • 66
10

Function to validate input:

validateInputs(text, type) {
let numreg = /^[0-9]+$/;
  if (type == 'username') {
    if (numreg.test(text)) {
      //test ok
    } else {
      //test not ok
    } 
  }
}



<TextInput
   onChangeText={text => this.validateInputs(text, 'username')}
/>

I hope this is helpful.

Javapocalypse
  • 2,223
  • 17
  • 23
Abdul Karim Khan
  • 4,256
  • 1
  • 26
  • 30
9
const [text, setText] = useState('');

const onChangeText = (text) => {
  if (+text) {
    setText(text);
  }
};

<TextInput
  keyboardType="numeric"
  value={text}
  onChangeText={onChangeText}
/>

This should save from physical keyboards

8

A kind reminder to those who encountered the problem that "onChangeText" cannot change the TextInput value as expected on iOS: that is actually a bug in ReactNative and had been fixed in version 0.57.1. Refer to: https://github.com/facebook/react-native/issues/18874

Wing
  • 81
  • 2
  • 2
5

Here is my other simple answer to accept only numbers in the text box using Regular Expressions.

onChanged(text){
    this.setState({ 
         myNumber: text.replace(/[^0-9]/g, '') 
    });
}
Venkatesh Somu
  • 4,710
  • 4
  • 23
  • 22
5
if (!/^[0-9]+$/.test('YourString')) {
  console.log('Enter Only Number');
} else {
    console.log('Success');
}
Adeel Iftikhar
  • 772
  • 7
  • 30
ANKIT DETROJA
  • 1,948
  • 1
  • 17
  • 24
4
<TextInput autoCapitalize={'none'} maxLength={10} placeholder='Mobile Number'  value={this.state.mobile} onChangeText={(mobile) => this.onChanged(mobile)}/>

and onChanged method :

onChanged(text){
   var newText = '';
   var numbers = '0123456789';
   if(text.length < 1){
     this.setState({ mobile: '' });
   }
   for (var i=0; i < text.length; i++) {
        if(numbers.indexOf(text[i]) > -1 ) {
             newText = newText + text[i];
        }
        this.setState({ mobile: newText });
    }
}
Bheru Lal Lohar
  • 880
  • 9
  • 17
  • Your solution, although solving my problem, produces warning: `"This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property 'type' on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See fb>react-event-pooling for more information."` I don't know what to do with this. Can you help? – Mike Jan 31 '17 at 15:57
  • @Mike some event might causing this, can you share your component code via jsfiddle or rnplay.org – Bheru Lal Lohar Feb 04 '17 at 06:16
  • I solved that shortly after commenting here, but unfortunately can figure what was the problem. – Mike Feb 08 '17 at 14:04
  • using a regex would be a smarter choice – Asif Ali May 03 '19 at 11:33
3

I had the same problem in iOS, using the onChangeText event to update the value of the text typed by the user I was not being able to update the value of the TextInput, so the user would still see the non numeric characters that he typed.

This was because, when a non numeric character was pressed the state would not change since this.setState would be using the same number (the number that remained after removing the non numeric characters) and then the TextInput would not re render.

The only way I found to solve this was to use the keyPress event which happens before the onChangeText event, and in it, use setState to change the value of the state to another, completely different, forcing the re render when the onChangeText event was called. Not very happy with this but it worked.

Gustavo Franco
  • 141
  • 1
  • 6
3

I wrote this function which I found to be helpful to prevent the user from being able to enter anything other than I was willing to accept. I also used keyboardType="decimal-pad" and my onChangeText={this.decimalTextChange}

  decimalTextChange = (distance) =>
{
    let decimalRegEx = new RegExp(/^\d*\.?\d*$/)
    if (distance.length === 0 || distance === "." || distance[distance.length - 1] === "."
        && decimalRegEx.test(distance)
    ) {
        this.setState({ distance })
    } else {
        const distanceRegEx = new RegExp(/^\s*-?(\d+(\.\d{ 1, 2 })?|\.\d{ 1, 2 })\s*$/)
        if (distanceRegEx.test(distance)) this.setState({ distance })
    }
}

The first if block is error handling for the event the user deletes all of the text, or uses a decimal point as the first character, or if they attempt to put in more than one decimal place, the second if block makes sure they can type in as many numbers as they want before the decimal place, but only up to two decimal places after the point.

Bee
  • 142
  • 17
Andrew M Yasso
  • 101
  • 1
  • 6
  • This is nice, except I cannot get any digits after the decimal point? It would be nice to allow a negative as well, e.g. -3.14 – gfmoore Sep 02 '21 at 16:50
2

Using a RegExp to replace any non digit. Take care the next code will give you the first digit he found, so if user paste a paragraph with more than one number (xx.xx) the code will give you the first number. This will help if you want something like price, not a mobile phone.

Use this for your onTextChange handler:

onChanged (text) {
    this.setState({
        number: text.replace(/[^(((\d)+(\.)\d)|((\d)+))]/g,'_').split("_"))[0],
    });
}
2

You can remove non numeric characters using regex

onTextChanged (text) {
    this.setState({
        myNumber: text.replace(/\D/g, ''),
    });
}
Anoop
  • 23,044
  • 10
  • 62
  • 76
2

In case anyone is looking for solution that allows to use numeric keyboard but also validates input to allow only one decimal point then below is what I wrote to accomplish that. I also wanted to have comma(,) replaced with dot(.) as that's the way I'm saving it to database but you can remove that - it's optional.

const [decimalInput, setDecimalInput] = useState('')
const validator = /^[+-]?\d*(?:[.,]\d*)?$/;

function onNumberInputChange(text){
        if (validator.test(text)){
            text = text.replace(",",".") //this is optional
            setDecimalInput(text);
        }
        else{
            //this will remove the last character as it didn't succeed validation
            setDecimalInput(text.substring(0, text.length - 1));
        }
    }

and text input component initialization like this:

<TextInput
                style={{textAlign: 'center'}}
                value = {decimalInput}
                onChangeText={(text) => {
                  setDecimalInput(text);
                  onNumberInputChange(text);
                }}
                placeholder={"type decimal value here..."}
                keyboardType="numeric"
                placeholderTextColor="#ccc">
</TextInput>

Maybe some of you will need same approach.

tommy_key
  • 21
  • 1
1

I've created a component that solves this problem:

https://github.com/amirfl/react-native-num-textinput

amirfl
  • 1,634
  • 2
  • 18
  • 34
1

For Decimal /Floating point number only try this

    onChangeMyFloatNumber(text){
let newText = '';
let numbers = '0123456789.';

for (var i=0; i < text.length; i++) {
    if(numbers.indexOf(text[i]) > -1 ) {
        newText = newText + text[i];
        if(text[i]=="."){
          numbers = '0123456789'
        }
    }
    else {
        // your call back function
        alert("please enter numbers only");
    }
}
this.setState({ MyFloatNumber: newText });

}

abinthomas12914
  • 739
  • 5
  • 8
1

If you are using hooks:

<TextInput
onChangeText={text=> setMyNumber(text?.replace(/[^0-9]/g, ''))}
....
/>

assuming the text is initialise using useState hook

const [myNumber,setMyNumber]=React.useState('');
FortuneCookie
  • 1,756
  • 3
  • 25
  • 41
1

if you add following lines then it will open number-pads only not characters-pads when you start typing:

 <TextInput
          placeholder="Enter Mobile No."
          onChangeText={setMobileNumber}
          value={mobileNumber}
          keyboardType="number-pad"
          maxLength={12}
        />
sherkhan
  • 811
  • 8
  • 8
0

i think,

onChangeText((val:string) => 
    isNaN(Number(val))
       ? val.substr(0, val.length - 1)
       : val
)
// 0     > true
// 0.    > true
// 0.3   > true
// -     > false
// .     > false
// 0.4-  > false
// -1.3  > true

this code will be check "is this a not a number?" and, if last char is wrong delete last word.

0

You can achieve this by using keyboardType="numeric" // This sets the keyboard to numeric or Regular Expressions

import React, { useState } from 'react';
import { View, TextInput, StyleSheet } from 'react-native';

const NumericTextInput = () => {
  const [text, setText] = useState('');

  const handleTextChange = (inputText) => {
    // Remove any non-numeric characters using regular expression
    const numericText = inputText.replace(/[^0-9]/g, '');
    setText(numericText);
  };

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        value={text}
        onChangeText={handleTextChange}
        keyboardType="numeric" // This sets the keyboard to numeric
        placeholder="Enter numeric value"
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  input: {
    width: 200,
    height: 40,
    borderWidth: 1,
    borderColor: 'gray',
    paddingHorizontal: 10,
  },
});

export default NumericTextInput;
-1

change your key board type to numeric

<TextInput
onChangeText={this.onChangeText}
 keyboardType={"numeric"}
/>

then replace dot with space

onChangeText=(value)=>{
    let text = value.replace(".", '');
              if (isNaN(text)) {
                // Its not a number
                return
              }
    
    console.log("text",text)
    }
cain
  • 709
  • 2
  • 10
  • 18