I have made an Input component. If it is a number I want to format it correctly, like a currency. I.e. 4000 would be 4,000.
Here is a codesandbox.
I am having issues with displaying and updating this.
<Input initialValue={'400000000'} isNumber={true} />
My Input component looks like this.
type Props = {
initialValue?: string;
isNumber?: boolean;
};
const Input = ({ initialValue = '', isNumber }: Props) => {
const [value, updateValue] = useState(initialValue);
const update = (val: any) => {
if (isNumber) {
const x = Number(val);
updateValue(x.toLocaleString());
} else {
updateValue(val);
}
};
return (
<StyledInput
type="text"
value={value}
onChange={e => update(e.target.value)}
/>
);
};
I am seeing an error NaN in my input component. Anyone have any ideas?
Javascript has a number formatter (part of the Internationalization API).
// Quick Solution With Intl.NumberFormat
const update = (val: any) => {
var formatter = new Intl.NumberFormat("en-US"); // Intl language tag,
updateValue(formatter.format(val.replace(/,/g, ""))); //Remove ',' to format number again
};
Code Snippet:
// Intl.NumberFormat With React State Update
var currentVal = 0;
...
const update = (event: any) => {
/**
https://stackoverflow.com/questions/35535688/stop-cursor-jumping-when-formatting-number-in-react
https://github.com/facebook/react/issues/955
*/
const caret = event.target.selectionStart
const element = event.target
window.requestAnimationFrame(() => {
element.selectionStart = caret
element.selectionEnd = caret
})
// -- Stop cursor jumping when formatting number in React
var val = event.target.value.replace(/(\..*)\./g, '$1') //Replace Multiple Dot(.)
var x = Number(val.replace(/,/g, ""));
if (currentVal != x) {
var formatter = new Intl.NumberFormat("en-US", { minimumFractionDigits:2});
currentVal = formatter.format(x);
updateValue(currentVal);
}else{
updateValue(val);
}
};
return (<input type="text" value={value} onChange={e => update(e)} />);
Note : Code Snippet gives you an idea to format numbers, You need to handle few more use-cases for production.
Also check the react-number-format, Which may suit for your application.
Reference :
Intl.NumberFormat vs Number.prototype.toLocaleString
How can I format numbers as dollars currency string in
JavaScript?
Intl.NumberFormat | MDN
The problem is in
const x = Number(val);
when you evaluate Number("32,423,343"), a string including commas Js will throw an error...
The correct way would be sending the number without commas.. Number("32432343")
To solve it you can add this line to remove the commas, before evaluating to a Number..
val = val.replace(/,/g, '');
https://codesandbox.io/s/r0vm8nxvjo
Related
I want to convert comma-separated values into tags. In fact I already have a good part done, but I have a problem. the tags come "added" with the previous ones. If I type in the input "ex1,ex2", the tags "ex1" and "ex1,ex2" will be created, but the tags that should be created are "ex1" and "ex2". look: .
this is my input:
<Input
onChangeText={(text: string) => handleTagChange(text)}
onKeyPress={handleTagKey}
value={inputTag}
/>
and here is the functions:
const handleTagChange = (text: string) => {setInputTag(text);}
const handleTagKey = (event: NativeSyntheticEvent<TextInputKeyPressEventData>) =>
{
const { key } = event.nativeEvent;
const trimmedInput = inputTag.trim();
if(key === ',' && trimmedInput.length && !tags.includes(trimmedInput)) {
event.preventDefault();
setTags((prev) => [...prev, trimmedInput]);
}
}
I thought it would be enough to set an empty string in the input value after setTags (setInputTag("")), but apparently it's not that simple.
would this work for you?
setTags(trimmedInput.split(","));
you must first remove the last comma
This question already has answers here:
HTML input type="number" still returning a string when accessed from javascript
(9 answers)
Closed 1 year ago.
The default state is a number, adding and subtracting from the default state works as expected. When the input value is changed by typing a number in the input field the state changes to a string. What's wrong here?
const [value, setValue] = useState(1);
const handleChange = e => {
setValue(e.target.value)
}
const handleAdd = () => {
setValue(value+1)
}
const handleSubtract = () => {
setValue(value-1)
}
return (
<input type='number' value={value} onChange={handleChange} />
<button label='+' onClick={handleAdd} />
<button label='-' onClick={handleSubtract} />
)
Based on this:
The purpose of the number type is that mobile browsers use this for showing the right keyboards and some browsers use this for validation purposes.
To fix this, there is some approaches. But I suggest to you to parseInt the value when onChange. So:
const [value, setValue] = useState(1);
const handleChange = (e) => {
setValue(parseInt(e.target.value));
};
const handleAdd = () => {
setValue((prev) => prev + 1);
};
const handleSubtract = () => {
setValue((prev) => prev - 1);
};
return (
<div>
<input type="number" value={value} onChange={handleChange} />
<button label="+" onClick={handleAdd} />
<button label="-" onClick={handleSubtract} />
</div>
);
What's wrong here?
e.target.value is a string, even if you type a number, you will be given a string in your event
setValue(e.target.value)
Your state now has a string inside it
setValue((prev) => prev - 1);
Your add and subtract methods now are working with a string and a number, you are now victim of coercion
https://www.freecodecamp.org/news/js-type-coercion-explained-27ba3d9a2839/
'2' + 1 = 21, so after your state becomes a string, your code won't work as you intended
You need to use parseInt as written in another answer, or parse in any way you want, but always make sure your state has the type you intended
const handleChange = e => {
setValue(Number(e.target.value) || 0)
}
That is another way, note that when parsing a string that cannot become a number you get NaN, the above code will make it become 0 instead, but you can handle it your way
I have a problem. I try to convert my string dynamically, but for some reason, it only converts the first letter of my initial string. What can I do to solve this?
Ex:
input: Mike
String_1 = 'Mike'
String_2 = 13 (I want it to be 139115, 13 for M, 9 for I, 11 for k and 5 for e).
This is my code:
import './App.css';
import React, {useState} from 'react';
import emojies from './emojies';
function App() {
let [String_1, setString_1] = useState( '' );
let [String_2, setString_2] = useState( '' );
let [Index, setIndex] = useState();
return (
<div>
<div>
<input
type="text"
value={String_1}
placeholder="Enter a message"
onChange={e => {
const val = e.target.value;
setString_1(val);
setString_2(val.toLowerCase().charCodeAt(0)-96);
}
}
/>
</div>
<div>
<p>{String_1}</p>
<p>{String_2}</p>
</div>
</div>
);
}
export default App;
This has nothing to do with React or setState.
Your issue is the logic around generating that String_2.
As you can see from the below snippet, your val.toLowerCase().charCodeAt(0)-96 only returns 13, so setState is acting correctly by passing on that value.
const val = 'Mike'
const String_2 = val.toLowerCase().charCodeAt(0) - 96
console.log(String_2)
const correct = parseInt(val.toLowerCase().split('').map(x => x.charCodeAt() - 96).join(''))
console.log(correct)
The new logic splits the string up into chars, maps over each of them to create a list of chars, then joins them together and converts into a int.
You make a mistake on setting the value for String_2.
Try this.
setString_2(val.split("").map(c => {
return c.toLowerCase().charCodeAt(0) - 96;
}).join(""));
Pass your string value from input box to this function. It iterates over all the alphabets from the string and convert them to their idx + 1 and join everything.
const convertStringToCharIdxString = (str) => {
const calcCharIdx = (char) => char.charCodeAt(0) - 97 + 1; // + 1, because you are considering a as 1
return str.split('').map(calcCharIdx).join('');
}
Just creating a simple currency converter (React + Typescript).
Everything works good. But I'm bit misunderstanding 1 thing: how to clear input (where you enter amount of money) dynamically properly?
Code:
const App = () => {
...
const [amount, setAmount] = useState(1)
const [amountInFromCurrency, setAmountInFromCurrency] = useState(true)
let fromAmount, toAmount
if (amountInFromCurrency) {
fromAmount = amount
toAmount = parseFloat((amount * exchangeRate).toFixed(2))
} else {
toAmount = amount
fromAmount = parseFloat((amount / exchangeRate).toFixed(2))
}
return (
<Card
amount={fromAmount}
/>
<Card
amount={toAmount}
/>
)
}
const Card = ({ amount }) => {
return (
<input type="number" value={amount || 0} onChange={changeInput}/>
)
}
When I start clean the input to the end it becomes 0 in both inputs as expected. But when I start entering the numbers in the input after cleaning it (when it shows 0) they start appearing after 0 but not instead. Example: shows "0", I start entering the numbers, shows "0123" instead "123". The 0 disapears only if I highlight all the numbers with cursor and enter another number or it disappears if I push inner/outer-spin-buttons in input.
As you can see my approach here for input value is value={amount || 0}. If I put only value={amount} the mistake that 'You have NaN in value' appears when I clean input to the end. I don't need that mistake.
What I need: cleaning inputs fully without mistake or if I show 0 after cleaning I need to disapper it correctly when I start entering other numbers like "123" but not "0123"
UPDATE:
Code for input change:
const handleFromAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setAmount(parseInt(e.target.value))
setAmountInFromCurrency(true)
}
const handleToAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setAmount(parseInt(e.target.value))
setAmountInFromCurrency(false)
}
What is the best way to change each element of an array based on the length of the array?
For example:
User #1 input = "XYZVC"
Expected Output = "BLABL"
User #2 input = "XYZVCAD"
Expected Output = "BLABLAB"
I want B to replace index[0], L to replace index[1], and A to replace index[2]. The part I'm struggling with the most is also getting it to repeat if the user input is longer than 3.
My Attempts
I've attempted to split() the input into an array, shift() it and push() it into a new array without any luck.
I've pasted in my code below for even more detail:
import React from 'react'
class UserInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
handleChange = (event) => {
this.setState({value: event.target.value})
}
handleSubmit = (event) => {
alert('Your input has been submitted: ' + this.state.value)
event.preventDefault()
}
badRobot = () => {
//Check length of user input
//Add B, L, A, matching the length of the user input
//Store it within a new variable
//Plug that into the bad robots output
let checkedInput = this.state.value
let checkedArray = checkedInput.split('')
const newArr = checkedInput.shift()
}
render(){
return(
<div>
<form onSubmit={this.handleSubmit}>
<label>
<p>Say Something</p>
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<div>
<h3>Good Robot</h3>
<p>I hear you saying {this.state.value}. Is that correct?</p>
<h3>Bad Robot</h3>
<p>.</p>
<h3>Kanyebot 5000</h3>
<p>I'm gonna let you finish but Beyonce is {this.state.value}.</p>
</div>
</div>
)
}
}
export default UserInput
UPDATE
As it turns out, there is a string function, padStart (works with padEnd too) that will do exactly that pretty easily :
const input1 = "XYZVCAD"
const input2 = "erkjguherkluagheruhg"
function mask(str){
return ''.padStart(str.length, 'BLA');
}
console.log(mask(input1))
console.log(mask(input2))
One way to achieve it is to use the repeat function and then cut out your string depending on the original string length :
const input1 = "XYZVCAD"
const input2 = "erkjguherkluagheruhg"
function mask(str){
return 'BLA'.repeat(str.length / 3 + 1).substring(0, str.length)
}
console.log(mask(input1))
console.log(mask(input2))
The repeat function will repeat your string x times, as you will see in the docs linked above. Here, since your string is 3 characters long, we only need to repeat your string depending on the length of the original one, divided by 3. I am adding one since a division like 5/3 will be rounded to 1, leading to BLA even though your string is longer.
The last step, substring, will simply cut your string to the exact same length as the original one.
Here is another way of achieving it, by splitting your string into an array, and giving it the correct letter by using the modulus operator :
const input1 = "XYZVCAD"
const input2 = "erkjguherkluagheruhg"
function mask(str){
return str.split('').map((ch, index) => 'BLA'[index % 3]).join('')
}
console.log(mask(input1))
console.log(mask(input2))
This isn't code, but it's the approach I would take:
Split the input into a char array
For loop through it, with index starting at 0
If index + 1 MOD 3 = 0, set value to 'A' (array[i+1]%3 === 0)
If index + 1 MOD 2 = 0, set value to 'L'
Otherwise set value to 'B'
Return a string of the joined array items (array.join)
var myString = "somestringofsomelength";
var replacementsArr=["B","L","A"];
function replacer(str){
repString="";
for(var i=0; i<str.length;i++){
repString+=replacementsArr[i % replacementsArr.length];
}
return repString;
}
console.log(replacer(myString));
That's the way I would do it. It's using remainder division to partition the string from being 12345678 in index relative to replacementsArr to 12312312
You could change it straight away
const letters = ['B','L','A'];
handleChange = (event) => {
const length = event.target.value.length;
const letter = letters[length%3];
this.setState({value: this.state.value+letter});
}