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"
/>
);
};
Related
Everytime I type, i lose the focus of the input text. I have other components which are working fine; but this one is losing focus. Any idea why would this happen?
I tried creating separate components and rendering them in my switch but still not working.
I have other components rendered with Switch statements and are working fine.
const FactionDetail = ({Faction}) => {
const [tab, setTab] = React.useState(0)
const [selectedMember, setSelectedMember] = React.useState(null)
const [receipt, setReceipt] = React.useState('')
const [selectedRank, setSelectedRank] = React.useState(null)
const [newRankDetails, setNewRankDetails] = React.useState({
rankName: '',
wage: 0,
hasPerm: false
})
const menuOption = (tabId) =>{
setTab(tabId)
}
const selectMember = (e) =>{
setSelectedMember(e)
}
const renderDropdownOptions = () =>{
let i = 0
for (const rank in Faction.ranks){
i++
return <option key = {i} value = {rank}>{rank}</option>
}
}
React.useEffect(() =>{
if(selectedMember != null && selectedRank != selectedMember.rank){
setSelectedRank(selectedMember.rank)
}
}, [selectedMember])
const RightBody = () =>{
switch(tab){
case 0: // Dashboard
return <>
</>
case 1: //Members
return <> </>
case 2: //Manage
return <>
<div className ='CreateRanks'>
<label>Rank Name</label>
<input style ={{width:'80px'}} onChange = {(e) => setNewRankDetails((prev) => {return {...prev, rankName: e.target.value}})} value = {newRankDetails.rankName}></input>
<label>Wage</label>
<input style ={{width:'80px'}} onChange = {(e) => setNewRankDetails((prev) => {return {...prev, wage: e.target.value}})} value = {newRankDetails.wage}></input>
<label>Has Perm</label>
<input type="checkbox" onChange = {(e) => setNewRankDetails((prev) => {return {...prev, hasPerm: e.target.checked}})} value = {newRankDetails.hasPerm} />
<button onClick = {() =>{ updateClient('factions:createNewRank', newRankDetails)}}>Create</button>
</div>
</>
default:
return<>{tab}</>
}
}
I was rendering two compoenents in the 'main' component; therefor the data from the useStates were mixing and re-rendering.
<input style ={{width:'80px'}} onChange = {(e) => saveDetails(e, 'Rank Name')} value = {newRankDetails.rankName}></input>
const saveDetails=(e, param:string)=>{
e.preventDefault();
if(param==='Rank Name'){
setNewRankDetails((prev) => {...prev, rankName: e.target.value})
}else if(param==='Wage'){
setNewRankDetails((prev) => {...prev, wage: e.target.value})
}
}
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>
);
I have an input field that takes in a number.(between 1 and 30) I want to display an array of items depending on what number is placed in that text field. how can this been done with React hooks. I have something basic for a start like this, but this might not even be the best way to start this.
export default function App() {
const [state, setState] = React.useState({ value: "" });
const [myArray, updateMyArray] = React.useState([]);
const onSubmit = () => {
updateMyArray((arr) => [...arr, `${state.value}`]);
};
const handleChange = (event) => {
let { value, min, max } = event.target;
value = Math.max(Number(min), Math.min(Number(max), Number(value)));
setState({ value });
};
return (
<>
<input
type="number"
onChange={handleChange}
value={state.value}
min={""}
max={100}
/>
<button onClick={onSubmit}>Confirm</button>
{state.value && (
<>
<div>
{myArray?.map((e) => (
<div>{e}</div>
))}
</div>
</>
)}
</>
);
}
You can do it like this
updateMyArray(new Array(state.value).fill(""));
This will create a new array with the length of state.value and asign it to myArray
Maybe this example will be helpful for you.
function App() {
const [amount, setAmount] = useState(0);
const [submittedAmount, setSubmittedAmount] = useState(0);
// optionally
const onSubmit = () => {
setSubmittedAmount(amount);
};
const handleChange = (event) => {
let { value, min, max } = event.target;
value = Math.max(Number(min), Math.min(Number(max), Number(value)));
setAmount(value);
};
return (
<>
<input
type="number"
onChange={handleChange}
value={amount}
min={0}
max={100}/>
<button onClick={onSubmit}>Confirm</button>
{ /* you can use amount instead of submitted amount if you want */
{submittedAmount > 0 && Array.from({ length: submittedAmount }, (_, index) => <div key={index}>{index}</div> )}
</>
);
}
In my opinion if you can skip submitting and use only amount state. Thanks to this your UI will change automatically after input value change without submitting.
If you know the value of value, you can loop till that number, before the render, like:
const items = [];
for (let i; i < state.value; i++) {
items.push(<div>{i}</div>);
}
return (
<div>
{items}
</div>
)
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>
);
}