How to format the TextInput component value in react native? - javascript

I use this function to format every number input I have :
export const parseToNumber = (value: string) => {
const numberRegex = /^[0-9\b]+$/;
if (numberRegex.test(value)) {
return value;
} else {
return value.replace(/\D/g, "");
}
};
And I use it with another function to have a an empty space between every 4 characters like this :
onChangeText={(e) => {
setFieldValue(
"phone",
parseToNumber(e).replace(/(.{4})/g, "$1 ")
);
}}
The problem is the Backspace won't work because of the parseToNumber . How can I exclude the functions when the user presses the Backspace and use the simple way like this setFieldValue("phone", (e)); so the backspace works ?
Or is there another way to fix the Backspace disabled problem with these functions in place possibly modify the parseToNumber function ?

If Problem is parseToNumber, you can make sure only take numbers by opening numbers only keyboard by passing prop
keyboardType = 'numeric'
or to simplify the use and support multiple spacing for input you can use this library - react-native-mask-input, its js only library so you can also just copy the specific implementation on need to use basis.

Related

`toLocaleString()` not rendering inside number input when value greater than 1 million

In this code, I have the value 1000000 (one million) which I format with toLocaleString('en-gb') to get 1,000,000.
Then I print that value as text and it works as expected, also when I use that const as the value of a text input.
But when using the value in a numeric input, it just doesn't render. It works though when the value is < 1 million.
Inspecting the html, I see the value is correct:
In addition, when trying to type the value in that numeric input, it doesn't register values after 4 digits.
Any ideas what's going on? I wonder if it could be that after 999.000 the number will have two thousand-separators
teh codez (also in this playground https://stackblitz.com/edit/react-ts-8ufbe1?file=App.tsx):
export default function App() {
const value = (1000000).toLocaleString('en-gb');
const [inputValue, setInputValue] = React.useState(value);
return (
<div>
<h1>{value}</h1>
<input
type="number"
value={inputValue}
onChange={(e) => setInputValue(Number(e.target.value).toLocaleString('en-gb'))}
/>
<input type="text" value={inputValue} />
</div>
);
}
I see there are libraries like react-number-format but seems like the native way should do what I need.
Thank you very much in advance.
It works before reaching 1,000 because 1,000 is where you have to add a comma. At 999 you still satisfy the requirements of an input with type number you're inserting only a number 999. Since HTML only really allows strings they let you pass with "999" vs 999 the pure numeric version.
In any case. I don't think you want to use the type='number' it works pretty poorly and shows the ticker up and down. If this is a mobile focused decision consider using type='tel'.
What you can also do is attempt to manually mask the behavior by putting 2 inputs on top of each other. Or you can use an input type='tel' onFocus={() => Number(inputValue)} and onBlur={() => inputValue.toLocaleString('en-gb')} and that will be the start to getting where you want to get.
This type of stuff usually is slightly messy and will take a bit to perfect. The UI can easily get wonky, and the problem is not so perfectly solved:
We want to enforce that users only type numbers and make it easy for them to do so (especially mobile)
We want the numbers to appear as strings.
Those 2 priorities are at odds with each other.
If you don't want to directly import a library which contains this I'd strongly suggest you read their source code.
Some potential resources:
https://cchanxzy.github.io/react-currency-input-field/ (Note that just the single component is 600 lines long)
including this excerpt:
const handleOnFocus = (event: React.FocusEvent<HTMLInputElement>): number => {
onFocus && onFocus(event);
return stateValue ? stateValue.length : 0;
};
/**
* Handle blur event
*
* Format value by padding/trimming decimals if required by
*/
const handleOnBlur = (event: React.FocusEvent<HTMLInputElement>): void => {
const {
target: { value },
} = event;
const valueOnly = cleanValue({ value, ...cleanValueOptions });
if (valueOnly === '-' || !valueOnly) {
setStateValue('');
onBlur && onBlur(event);
return;
}
const fixedDecimals = fixedDecimalValue(valueOnly, decimalSeparator, fixedDecimalLength);
const newValue = padTrimValue(
fixedDecimals,
decimalSeparator,
decimalScale !== undefined ? decimalScale : fixedDecimalLength
);
const numberValue = parseFloat(newValue.replace(decimalSeparator, '.'));
const formattedValue = formatValue({
...formatValueOptions,
value: newValue,
});
if (onValueChange) {
onValueChange(newValue, name, {
float: numberValue,
formatted: formattedValue,
value: newValue,
});
}
setStateValue(formattedValue);
onBlur && onBlur(event);
};
```

How can I mask any element, not only inputs, in React

I'm making an admin dashboard with NextJS and MaterialUI (mui), and I need to mask some values (eg.: phone numbers) that comes from back-end without mask.
I'm using react-input-mask for input elements, but I don't know how to mask any element (for example, a span inside a table cell).
For example, my back-end gives me a phone number like 5511912345678 and a document number like 12345678900, and I need to display it in a table this way:
First name
Last name
Phone number
Document
John
Doe
+55 (11) 9.1234-5678
123.456.789-00
Lorem
Ipsum
+55 (11) 1234-5678
234.567.891-00
How can I mask that values on a table-cell without an <input> element? I assume that will not be possible using react-input-mask, so there's no problem adding another package. I just don't know what package to install to achieve this result.
Thanks!
You can either use a library from NPM or build your own masking function. Here is a naïve implementation of the masking functions. Here is a working example and following are the important snippets from it.
digitsExtractor will return a wrapped object which will help in pulling out digits from the number.
function digitsExtractor(numberString) {
const digitsInReverse = numberString.split('').reverse();
const extractDigits = (count = 0) => {
const digits = [];
while (count-- > 0) digits.push(digitsInReverse.pop());
return digits.join('');
};
return {
extractDigits,
getRemainingDigitsCount: () => digitsInReverse.length,
};
}
maskPhoneNumber will mask the phone number
function maskPhoneNumber(phoneNumber) {
const { extractDigits, getRemainingDigitsCount } =
digitsExtractor(phoneNumber);
return `+${extractDigits(2)} (${extractDigits(2)}) ${
getRemainingDigitsCount() === 8
? [extractDigits(4), extractDigits(4)].join('-')
: getRemainingDigitsCount() === 9
? `${extractDigits(1)}.${extractDigits(4)}-${extractDigits(4)}`
: ''
}`;
}
maskDocumentNumber will mask the document number
function maskDocumentNumber(documentNumber) {
const { extractDigits } = digitsExtractor(documentNumber);
return [
[extractDigits(3), extractDigits(3), extractDigits(3)].join('.'),
extractDigits(2),
].join('-');
}
You can make it more generic based on your requirements.

Regex Validation for userInput in React

I am trying to implement a search using regex . To validate if the entered value is a valid regex in the search box I am using the source code from a library regex-validate (REGEX VALIDATION LIBRARY - regex-regex)
If the entered value is a valid regex then I am Parsing it to a regular expression using the source code from this library Regex-Parse) (PARSING LIBRARY - Regex Parser) to filter/search using the parsed regex.Here is a code snippet for the same
import { useState } from "react";
import "./styles.css";
import { re, RegexParser } from "./regexValidation";
export default function App() {
const [val, setVal] = useState("");
const [validRegex, setValidRegex] = useState(false);
const validateRegex = (val: string) => {
setVal(val);
if (val === "") {
setValidRegex(false);
return;
}
// to check if the entered value(val) is a valied regex in string
if (re.test(val)) {
setValidRegex(false);
// parsing the entered value(val) to a regularexpression
const convertToRegex = RegexParser(val);
//filtering logic to filter based on convertToRegex variable
} else {
setValidRegex(true);
}
};
const handleChange = (e: any) => {
validateRegex(e.target.value);
};
return (
<div className="App">
<input value={val} onChange={handleChange}></input>
<h1>{validRegex ? "inValidRegex" : "ValidRegex"}</h1>
</div>
);
}
CodeSandBox link for the regex search RegexValidationSearch
I am facing an issue when the user enters '/?/' or '/*/' the re.test(val) returns true thereby implying that it is a valid regex but when it is trying to get parsed that is this line of code const convertToRegex = RegexParser(val) it throws the following errorRegexError
Is there any way to fix this such that this line of code re.test(val) returns false when the user enters any invalid regular expression there by implying that it is an invalid regex(in string format) and hence there is no need to parse it to a regular expression
This looks like it might be an incompatibility between the two libraries you are using (ie, they have different ideas of what valid Regex is).
Easiest way to fix this (and honestly the safest too, since you're dealing with user input) is to wrap your entire parsing logic with a try/catch like this:
// to check if the entered value(val) is a valied regex in string
if (re.test(val)) {
let convertToRegex;
try {
convertToRegex = RegexParser(val);
setValidRegex(true); // only set this AFTER a successful parse.
// also note I've swapped the true / false value here.
} catch (e) {
setValidRegex(false);
}
if (convertToRegex) {
// TODO: implement filtering logic based on convertToRegex variable
}
} else {
// NOTE: it didn't parse correctly, shouldn't this be false?
setValidRegex(false); // I've changed it for you
}
Also I think(?) you've made a couple errors in how you're handling setValidRegex which I've corrected in code. Don't be optimistic and say the user input is valid regex when you haven't actually confirmed (by creating a RegexParser) that it is!
With this approach there's an argument for deleting the re.test(val) and the other library entirely since you can get what you want by simply try/catch-ing. Up to you to decide if this is a decent choice for your codebase.

How to format number correctly in a calculator app (React)?

This is my first time posting in SO, and I need help formatting numbers correctly in a calculator app that I've made using ReactJS.
Here is the link on StackBlitz.
Now, I want to achieve the formatting effect after numbers are pressed and shown in the display and arithmetic signs are added, especially when multiple arithmetics are used.
To illustrate my point, below is a sample of the current display:
123456 + 7890123 * 11111
And what I want to achieve is this:
123,456 + 7,890,123 * 11,111
I could only do this when displaying the result using the toLocaleString() function. Even then, if I pressed number/numbers and then clicking the result button twice, it will be crashed (as the display contains a comma, and the evaluation function will not process it properly).
Hopefully, someone can point me out in the right direction. Thanks.
Quick Fix
You can remove ',' before evaluating the result.
Change line 65 of Input.js to
setDisplay(evaluate(display.replace(/,/g, '')).toLocaleString());
Better Solution
Keep separate variables for Internal logical state and External Display state, where the former is valid for code and the latter is its visual representation.
You can achieve this by useEffect like this
/* --- Display.js --- */
const Display = ({ display }) => {
const [printValue, setPrintValue] = useState('')
useEffect(() => {
setPrintValue(`${display}`.replace(/[0-9]+/g, num => (+num).toLocaleString()))
}, [display])
return (
<StyledDisplay>
{' '}
<span>{printValue}</span>{' '}
</StyledDisplay>
);
};
Also, in Input.js, update line 65 in handleResult to
setDisplay(evaluate(display));
For this kind of situations, I like to use regex. Here what you can do is to use a regex that matches 3 digits and add the comma as wanted. To simplify the regex I usually reverse the string:
const original = "123456 / 98765 * 22222"
function format(str) {
const reversed = str.split('').reverse().join('')
const formatted = reversed.replace(/(\d{3})(?=\d)/gm, `$1,`)
return formatted.split('').reverse().join('')
}
console.log('original string : ', original)
console.log('result string : ',format(original))
You can use this function in your Display component, just before injecting the display prop like this
function format(str){
const reversed = str.split('').reverse().join('')
const formatted = reversed.replace(/(\d{3})(?=\d)/gm, `$1,`)
return formatted.split('').reverse().join('')
}
const Display = ({ display }) => {
return (
<StyledDisplay>
{' '}
<span>{format(display)}</span>{' '}
</StyledDisplay>
);
};

Force TextField with type number to display dot as decimal separator

I am using material-ui to render a TextField component in my react app. It's somehow forcing all the <TextField type="number /> to have decimal separator as comma (,) instead of dot (.) which is confusing for the targeted users.
Is there any way to force it to always show dot as the decimal separator, regardless of locale?
I have created a small example here. just try to enter a number with decimals and click outside and it'll convert it into comma. This is probably because of my current device locale, but still I want to force a dot for everyone.
For me, setting the language attribute to "en-US" in the inputProps worked:
<TextField
type="number"
inputProps={{step: "0.1", lang:"en-US"}}
/>
As per my suggestion of using a third-party library to be integrated in the Textfield component; You could use the react-number-format library as an inputProp for the Textfield. It works because the number formatter offered by the aforementioned library has a decimalSeparator prop which defaults to the one character string "."
The custom number formatter would go like this:
import NumberFormat from 'react-number-format';
interface NumberFormatCustomProps {
inputRef: (instance: NumberFormat | null) => void;
onChange: (event: { target: { value: string } }) => void;
}
function NumberFormatCustom(props: NumberFormatCustomProps) {
const { inputRef, onChange, ...other } = props;
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={values => {
onChange({
target: {
value: values.value
}
});
}}
isNumericString
/>
);
}
And it could be used in a number Textfield like this:
<TextField
label="react-number-format"
value={values.numberformat}
onChange={handleChange("numberformat")}
id="formatted-numberformat-input"
InputProps={{
inputComponent: NumberFormatCustom as any
}}
type="number"
/>
For a fully functioning example, check out this sandbox.
I came out with solution that accepts both , and . separators using regex validator.
In addition of controlling input label, I control external numeric state <number | null>.
In my solution user can only input decimals separated . or , The input label and numeric state only changes if user input is convertable to float, In case of empty "" input, numeric state is set to null.
const [inputValue, setInputValue] = useState<string>("")
const [numericValue, setNumericValue] = useState<number | null>(null)
return (<TextField
inputMode={"numeric"}
onChange={(e) => {
e.preventDefault();
// if new value is valid float
if (/^([\d]*[,.]?[\d]{0,2})$/.test(e.target.value)) {
setInputValue(e.target.value);
const parsed = parseFloat(
e.target.value.replaceAll(",", ".")
);
setNumericValue(isNaN(parsed) ? null : parsed)
}
}}
value={inputValue}
/>);

Categories