I have a question about input type number in React.
I need to display dot separator if user enter number with comma.
Like this
11,2
Should convert to
11.2
How I can convert this number?I try
value.replace(/,/g, '.')
But this isn't working.I still see comma in my input.
PS:
This how I handle input
<input
type="number"
placeholder='Input'
name="inputValue"
step="0.01"
inputMode="decimal"
id='inputValue'
min="0"
value={inputValue}
onChange={handleChange}
/>
And this is my handleChange function
const handleChange = e => {
let { name, value } = e.target;
value = value.replace(/,/g, '.');
setData(prevState => ({ ...prevState, [name]: value}));
}
Try this code:
const Number = "11,1";
console.log(Number.replace(/\,/, "."));
//Now your handleChange function will look like this:
const handleChange = e => {
let { name, value } = e.target;
value = value.replace(/\,/, ".");
setData(prevState => ({ ...prevState, [name]: value}));
}
Related
Requirement - There will be two input fields in one row, one being name and second is number. In the second row, again two fields will be there with one being name and second is number. Now, the name field is readOnly and the second field number can be edited by user.
First time, the state which is in [{}] format would be empty. Now, when the user enters the number in the first row input, the state should get updated as [{ name: 'user1', number: 123}]. When the user enters value in row 2, the state should get updated as [{ name: 'user1', number: 123}, { name: 'user2', number: 456}]. If the user enters the value in row number 1 again, the existing object value should updated then adding a new one.
For example, if the user1's number gets updated to 789, the state value now should be [{ name: 'user1', number: 789 }, { name: 'user2', number: 456}].
Once this is achieved, if the numbers are duplicate, I should store and error in another state and with state, I can show on UI that the numbers are duplicate and need to have a unique number.
import { useState } from 'react';
const Test = () => {
const [data, setData] = useState([{}]);
const [error, setError] = useState(null);
const handleChange = (number, name) => {
//set data in the state
//validation
//if the values for number are duplicate for both field
//return an error for which input name the value is duplicate
}
return (
<div>
<input name='user1' value='user1' readOnly={true} />
<input name='number1' onChange={(e) => handleChange(e.target.value, 'user1')} />
<hr />
<input name='user2' value='user2' readOnly={true} />
<input name='number2' onChange={(e) => handleChange(e.target.value, 'user2')} />
</div>
)
}
Your handleChange function would look like this. Please not that I changed your initial data state to [] since a new user would simply be inserted instead of updating {}. I also prefer an empty state over null.
First we check whether the name already exists in data array. If not, we append it using curr callback.
If it exists and we only want to update the specific object, we can make use of map function.
By validating before updating the new user we can easily get the user that already has the value with find and set his name inside error state. Plus you can still decide if you want to update the data state if it has duplicate numbers by putting the // update value of exisiting user block inside of an else.
const [data, setData] = useState([]);
const [error, setError] = useState();
const handleChange = (number, name) => {
const idx = data.findIndex((item) => item.name === name);
if (idx === -1) {
// new user insert
setData((curr) => [...curr, { name: name, number: number }]);
} else {
// validation
const duplicateUser = data.find((item) => item.number === number);
if (duplicateUser) {
setError(duplicateUser.name);
console.log(`${duplicateUser.name} and ${name} have duplicate number`);
}
// update value of exisiting user
setData((curr) =>
curr.map((item) => {
return item.name === name ? { name: name, number: number } : item;
})
);
}
};
Look out code
import React, { useState } from "react";
export const Test = () => {
const [data, setData] = useState([]);
const [error, setError] = useState("");
const handleChange = (number, name) => {
setError("");
let isDuplicate = false;
if (!data.length) {
setData([{ name, number }]);
} else if (data.length == 2) {
const newData = data.map((val) => {
if (val.name === name) {
return { name, number };
} else {
isDuplicate = val.number === number;
}
return val;
});
setData(newData);
} else {
const existingData = data.find((val) => val.name === name);
if (existingData) {
setData([{ name, number }]);
} else {
let [user] = data;
if (user.number === number) {
isDuplicate = true;
}
setData((oldState) => {
return [...oldState, { name, number }];
});
}
}
if (isDuplicate) {
setError("Number is duplicate! Each field should have uniq number.");
}
};
return (
<div>
<div className="error">{error}</div>
<input name="user1" value="user1" readOnly={true} />
<input
name="number1"
onChange={(e) => handleChange(e.target.value, "user1")}
/>
<hr />
<input name="user2" value="user2" readOnly={true} />
<input
name="number2"
onChange={(e) => handleChange(e.target.value, "user2")}
/>
</div>
);
};
I hope this will help you. Happy coding...
You have set handleChange event on onChange event so it will call when user enter the single input and check with existing value.
suggestion:- if you want to check whole value which is user enter and compare with the existing value so please use button and click event so it will check when user click after enter the whole value
Hope this will help you. !
import { useState } from "react";
const Test = () => {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const handleChange = (number, name) => {
const exists = data.find((e) => e.name === name);
if (!exists) {
setData([...data, { name, number }]);
} else {
if (exists.number === number) {
setError("Number already exists");
} else {
if (number) {
const index = data.findIndex((e) => e.name === name);
data[index].number = number;
setData([...data]);
}
}
}
};
console.log(data, error);
return (
<div>
<input name="user1" value="user1" readOnly={true} />
<input
name="number1"
onChange={(e) => handleChange(e.target.value, "user1")}
/>
<hr />
<input name="user2" value="user2" readOnly={true} />
<input
name="number2"
onChange={(e) => handleChange(e.target.value, "user2")}
/>
</div>
);
};
This might work for you [Updated]
export const Test = () => {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const handleChange = (number, name) => {
setData((prev) => {
const newState = [...prev];
const index = prev.findIndex((entry) => entry.name === name);
if (index < 0) {
return [
...prev,
{
name,
number
}
];
} else {
const dupEntry = prev.find(
(entry) => entry.name !== name && entry.number === number
);
if (dupEntry) setError(new Error(`${dupEntry.name} already has that value`));
else newState[index].number = number;
}
return newState;
});
//validation
//if the values for number are duplicate for both field
//return an error for which input name the value is duplicate
};
return (
<div>
<input name="user1" value="user1" readOnly={true} />
<input
name="number1"
onChange={(e) => handleChange(e.target.value, "user1")}
/>
<hr />
<input name="user2" value="user2" readOnly={true} />
<input
name="number2"
onChange={(e) => handleChange(e.target.value, "user2")}
/>
</div>
);
};
In the middle of a slightly long component which is basically a split date input (3 inputs for DD/MM/YYYY) - I think my approach is correct for the most part, but I'm struggling with my getDate function. The idea is that the currentDate is passed into this function (whatever has been entered in the keyboard), like so:
const handleValueChange = (date?: string) => {
onValueChange(getDate(date));
};
In the getDate function I want to take the values from selectedDay, selectedMonth & selectedYear and assign them to variables so that they can then be joined using the .join method and a valid date like "28-08-2022" can be returned.
Can anyone advise on a best possible approach?
const getDate = (date?: string): string => {
if (date) {
/* need to take value for DD/ MM / YYYY and store them in
individual variables that can then be used in an array or
something similar with .join to return a
joined up date like "28-09-2022" */
}
return "";
};
const DateInput = ({
dateInputValue,
dateInputFormat,
className,
inFilterContext,
onChange,
onValueChange,
value = "",
type = "text",
readOnly = false,
disabled = false,
placeholder,
mindate,
maxdate,
...inputProps
}: Props) => {
const [intialDayValue, initialMonthValue, initialYearValue] =
dateInputValue.split("-");
const [selectedDay, setSelectedDay] = useState(intialDayValue || "");
const [selectedMonth, setSelectedMonth] = useState(initialMonthValue || "");
const [selectedYear, setSelectedYear] = useState(initialYearValue || "");
const handleValueChange = (date?: string) => {
onValueChange(getDate(date));
};
// handle date input change event - DAY
const handleDayChangeEvent = (e: SyntheticInputEvent<any>) => {
const currentDate = e.target.value;
setSelectedDay(currentDate);
handleValueChange(currentDate);
};
// handle date input change event - MONTH
const handleMonthChangeEvent = (e: SyntheticInputEvent<any>) => {
const currentDate = e.target.value;
setSelectedMonth(currentDate);
handleValueChange(currentDate);
};
// handle date input change event - YEAR
const handleYearChangeEvent = (e: SyntheticInputEvent<any>) => {
const currentDate = e.target.value;
setSelectedYear(currentDate);
handleValueChange(currentDate);
};
return (
<StyledInputGroup>
<label htmlFor={`${name}_day`}>
<span>Day</span>
<StyledInput
{...inputProps}
type={type}
maxLength="2"
disabled={disabled}
readOnly={readOnly}
value={selectedDay}
onChange={handleDayChangeEvent}
onValueChange={handleDateSelectDay}
/>
</label>
<label htmlFor={`${name}_month`}>
<span>Month</span>
<StyledInput
{...inputProps}
type={type}
maxLength="2"
disabled={disabled}
readOnly={readOnly}
value={selectedMonth}
onChange={handleMonthChangeEvent}
onValueChange={handleDateSelectMonth}
/>
</label>
<label htmlFor={`${name}_year`}>
<span>Year</span>
<StyledInput
{...inputProps}
type={type}
maxLength="4"
disabled={disabled}
readOnly={readOnly}
value={selectedYear}
onChange={handleYearChangeEvent}
/>
</label>
</StyledInputGroup>
);
Basically, I want a little box with only one digit, it "works" fine until you try to put multiple '0', then in the box, multiple 0000 persist.
The code
const InputBox = () => {
const [value, setValue] = useState(0);
const handleChange = el => {
const newValue = el.target.value;
console.log(newValue);
const lastNumber = parseInt(
newValue.toString().slice(-1),
);
console.log(lastNumber);
setValue(lastNumber);
};
return (
<input
type='number'
pattern='[0-9]'
min='0'
max='9'
id='numberInput'
maxLength={1}
minLength={1}
value={value}
onInput={handleChange}
// onChange={handleChange}
className='inputBox'
/>
);
};
export default InputBox;
How to make it so I have only one digit at any time?
Don't need to parse to int or to string, just slice and set value.
minLength and maxLength doesn't work in <input type="number">.
const InputBox = () => {
const [value, setValue] = useState(0);
const handleChange = (el) => {
let newValue = el.target.value;
if (newValue.length > 1) {
newValue = newValue.slice(0, 1);
}
setValue(newValue);
};
return (
<input
type="number"
pattern="[0-9]"
min="0"
max="9"
id="numberInput"
value={value}
onInput={handleChange}
className="inputBox"
/>
);
};
This could work out
const handleChange = el => {
const newValue = el.target.value;
const lastNumber = newValue.replace(/[^0-9]/g,'')
setValue(lastNumber);
};
return (
<input
type="text" maxlength="1"
value={value}
onInput={handleChange}
/>
);
If you don't mind the type coercion to string of your state (you can always Number(value) when you need a number type for computations) then you can simply slice off the last char of the value and if it ends up being a falsey ('', 0, null, undefined, NaN) value fallback to 0 to allow coercion back to "0" when assigning to the value prop of the input. Also, the minLength|maxLength|pattern attributes only work when doing field validation in a form.
const handleChange = (el) => {
const { value } = el.target;
setValue(value.slice(-1) || 0);
};
const InputBox = () => {
const [value, setValue] = useState(0);
const handleChange = (el) => {
const { value } = el.target;
setValue(value.slice(-1) || 0);
};
return (
<input
type="number"
min="0"
max="9"
id="numberInput"
value={value}
onChange={handleChange}
className="inputBox"
/>
);
};
I have multiple inputs where user fill these inputs with numbers. I need a way to sum up the values of these inputs when user types in any input.
My Code
const [values,set_values] = useState({
sales:'',
bank_deposit:'',
supply:'',
expenses:''
})
const values_handler = (e) => {
let name= e.target.name;
let value= e.target.value;
values[name]=value;
set_values(values)
// Calling the method to sum the value
calc_total(value)
}
const [total,set_total]=useState(0);
const calc_total = (value) => {
total +=value;
set_total(total)
}
<input type='number' onChange={value_handler} name='sales' />
<input type='number' onChange={value_handler} name='bank_deposit' />
<input type='number' onChange={value_handler} name='supply' />
<input type='number' onChange={value_handler} name='expenses' />
Problem
The problem with this is that the values are summed up each time the value of an input got a chane, so that if user enters 15, it sums 1 then 5 since the mehtod is executed when a change occurred on the input's value.
you must not mutate the state values.
state updates are async.
you need not use the value of each onChange instead use the state values to update total value
Assuming you want to sum sales, bank_deposit, supply and expeses values, you can get them from states do it like below
const [values,set_values] = useState({
sales:'',
bank_deposit:'',
supply:'',
expenses:''
})
const values_handler = (e) => {
let name= e.target.name;
let value= e.target.value;
const newValues = {
...values,
[name]: value
}
set_values(newValues)
// Calling the method to sum the value
calc_total(newValues)
}
const [total,set_total]=useState(0);
const calc_total = (newValues) => {
const { sales, bank_deposit, expenses, supply} = newValues;
const newTotal = parseInt(sales) + parseInt(bank_deposit) + parseInt(expenses) + parseInt(supply)
setTotal(newTotal)
}
<input type='number' onChange={value_handler} name='sales' />
<input type='number' onChange={value_handler} name='bank_deposit' />
<input type='number' onChange={value_handler} name='supply' />
<input type='number' onChange={value_handler} name='expenses' />
try this:
const [values,set_values] = useState({
sales:'',
bank_deposit:'',
supply:'',
expenses:''
})
const values_handler = (e) => {
let name= e.target.name;
let value= e.target.value;
set_values({...values , [name]: value})
// Calling the method to sum the value
calc_total(values)
}
const [total,set_total]=useState(0);
const calc_total = (values) => {
aux = 0
for (var key in values){
aux += values[key]
}
set_total(aux)
}
<input type='number' onChange={value_handler} name='sales' />
<input type='number' onChange={value_handler} name='bank_deposit' />
<input type='number' onChange={value_handler} name='supply' />
<input type='number' onChange={value_handler} name='expenses' />
Remarks:
Using Chris G. recommendations in the comments
const Solution = () => {
const [input_values, set_inputvalues] = useState({
sales: 0,
bank_deposit: 0,
supply: 0,
expenses: 0
});
const [total, set_total] = useState(0);
useEffect(() => {
const arrValues = Object.values(input_values);
const inputTotals = arrValues.reduce((accum, curr) => (accum += curr), 0);
set_total(inputTotals);
}, [input_values]);
const changeValues = ({ name, value }) => {
set_inputvalues({ ...input_values, [name]: parseInt(value) });
};
return (
<div>
<h1>{total}</h1>
<input
type="number"
onChange={({ target }) => changeValues(target)}
name="sales"
/>
<input
type="number"
onChange={({ target }) => changeValues(target)}
name="bank_deposit"
/>
<input
type="number"
onChange={({ target }) => changeValues(target)}
name="supply"
/>
<input
type="number"
onChange={({ target }) => changeValues(target)}
name="expenses"
/>
</div>
);
};
To some up some key gotchas which other's have mentioned:
Don't alter state directly, use a set_xxx to update state.
You set type="number" so default state has to be numeric, i.e. sales: 0
Remember to parse values to int if numeric.
I'd like to keep the two digits after a number ie 2.89 or 2.00. Google brought me to this answer to use .toFixed(2).
While that works great, it does not work well when entered input values:
const [ value, setValue] = useState()
const onChange = (value) => {
const float = parseFloat(value)
setValue(float.toFixed(2))
}
<input
type="number"
value={value}
onChange={({ target }) => onChange(target.value)}
/>
If I should type, say, "300", the input value stops at "3.00". I have to move the cursor before "3" to type "300". What's the best way to do this?
I expect the value to always show .33, .00 etc while having the ability to "free type". As I type this question, I feel I need to use onBlur to convert the value to .toFixed and not while typing?
You can use onBlur and add some checks in while setting value
export default function App() {
const [value, setValue] = useState('');
const onChange = (v) => {
if (!Number.isNaN(v)) {
setValue(v);
}
};
return (
<div className="App">
<input
type="number"
value={value}
step="1"
onChange={({ target }) => onChange(target.value)}
onBlur={e => setValue(Number(value).toFixed(2))}
/>
</div>
);
}
I would not try to set the decimal places on the number on the onChange but make an onBlur handler.
const TodoApp = ( ) => {
const [ value, setValue] = React.useState('');
const onBlur = (e) => {
const float = parseFloat(e.target.value)
setValue(float.toFixed(2))
}
return (
<input
type="number"
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={onBlur}
/>
);
}
Hi dude read this https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number
And try using step propety, i recommended
<input
type="number"
value={value}
step="1"
onChange={({ target }) => onChange(target.value)}
/>
and try if it works
export const InputElement = () => {
const [value, setValue] = React.useState(0);
const fixInt = (v) => {
setValue(Number(v).toFixed(2));
};
return (
<div className="App">
<input
type="number"
value={value}
step="1"
onChange={({ target }) => fixInt(target.value)}
/>
</div>
);
}