How to use toFixed properly while entering input value - javascript

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>
);
}

Related

How can I show a button when another element has focus?

I have multiple rows, each row contains two text inputs and a button. When user focuses on one of the inputs, the button should be shown. When elements lose focus, the button should become invisible once again. My best attempt:
const Input = ({inputRef}) => {
return (
<>
<h1>Input</h1>
<input type="text" ref={inputRef}/>
</>
)
}
export default () => {
const firstRef = useRef(null);
const secondRef = useRef(null);
const it = useRef(null);
const [editing, setEditing] = useState(false);
function handleClick(e) {
firstRef.current.focus();
}
function handleSave() {
console.log("saving!");
}
function checkFocus(e) {
if (!it.current.contains(document.activeElement)) {
setEditing(false);
} else {
setEditing(true);
}
}
useEffect(() => {
document.body.addEventListener("focus", checkFocus, true);
return () => {
document.body.removeEventListener("focus", checkFocus, true);
}
}, []);
return (
<div ref={it}>
<Input inputRef={firstRef}/>
<Input inputRef={secondRef}/>
<button type="button" onClick={handleSave} style={{visibility: editing ? "visible" : "hidden"}}>Save</button>
<button type="button" onClick={handleClick}>Edit</button>
</div>
)
}
Is there any better/more elegant and efficient way of achieving this?
Here's a solution to what you're attempting using only CSS, which in my opinion makes it more elegant (and ever so slightly more performant, but really this is negligible).
https://codepen.io/danny_does_stuff/pen/QWMprMJ
<div>
<input id="input1" />
<input id="input2" />
<button id="save-button">Save</button>
<button id="edit-button">Edit</button>
</div>
<style>
input#input2:focus + button#save-button {
visibility: hidden;
}
</style>
If you wanted to do it in a more React way, you could do what Marco B suggested in his answer
You can use onBlur and onFocus events.
This should work as expected, just adapt the logic on your component
EDIT
Edited the onBlur method.
const INITIAL_STATE = {
input: ''
}
export default function App() {
const [show, setShow] = useState(false);
const [value, setValue] = useState(INITIAL_STATE);
const handleChange = (e) => {
const { value, name } = e.target;
setValue(prevState => ({ ...prevState, [name]: value }))
}
const onBlur = () => {
if (!value.input) {
setShow(false)
}
}
return (
<>
<Input name="input" onChange={handleChange} value={value.input} onFocus={() => setShow(true)} onBlur={onBlur} />
{show && <button>TEST</button>}
</>
);
}
const Input = (props) => {
return (
<>
<h1>Input</h1>
<input {...props} type="text" />
</>
);
};

How to display a set number of jsx elements depending on number placed in an input field. React

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>
)

ReactJS: one digit input can end up with multiple '0's

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"
/>
);
};

onKeyDown get value from e.target.value?

What's wrong with my way to handle input in react? I want to detect keycode and prevent them to be entered into the input, but now below code doesn't seem working.
const Input = ({ placeholder }) => { const [inputValue, setInputValue] = useState("");
const handleKeyDown = e => {
console.log(e.target.value);
if ([188].includes(e.keyCode)) {
console.log("comma");
} else {
setInputValue(e.target.value);
} };
return (
<div>
<input
type="text"
value={inputValue}
onKeyDown={handleKeyDown}
placeholder={placeholder}
/>
</div> ); };
https://codesandbox.io/s/ancient-waterfall-43not?file=/src/App.js
you need to call e.preventDefault(), but also you need to add onChange handler to input:
const handleKeyDown = e => {
console.log(e.key);
if ([188].includes(e.keyCode)) {
console.log("comma");
e.preventDefault();
}
};
const handleChange = e => setInputValue(e.target.value);
...
<input
type="text"
value={inputValue}
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder={placeholder}
/>
When you update the value of input, you should use onChange().
But, If you want to catch some character and treat about that, you should use onKeyDown().
So, in your case, you should use both.
this is an example code about backspace.
const Input = (props) =>{
const [value, setValue] = React.useState('');
function handleChange(e){
setValue(e.target.value);
}
function handleBackSpace(e){
if(e.keyCode === 8){
//Do something.
}
}
return (
<div>
<input onChange={handleChange} onKeyDown={handleBackSpace} value={value} type="text" />
</div>
)
}
```
Pass the value from Parent to child. Please check the below code.
import React, { useState } from "react";
import "./styles.css";
const Input = ({ placeholder, inputValue, handleKeyDown }) => {
return (
<div>
<input
type="text"
value={inputValue}
onKeyDown={handleKeyDown}
placeholder={placeholder}
/>
</div>
);
};
export default function App() {
const [inputValue, setInputValue] = useState();
const handleKeyDown = e => {
console.log(e.keyCode);
console.log(e.target.value);
if ([40].includes(e.keyCode)) {
console.log("comma");
} else {
setInputValue(e.target.value);
}
};
return (
<div className="App">
<Input
placeholder="xx"
value={inputValue}
handleKeyDown={handleKeyDown}
/>
</div>
);
}
onKeyDown, onKeyUp, and onKeyPress contain the old value of the target element.
onInput event gets the new value of the target element.
check the below link I add some console log. which help you to understand which event contains the value
https://codesandbox.io/s/ecstatic-framework-c4hkw?file=/src/App.js
I think you should not use the onKeyDown event on this case to filter your input. The reason is that someone could simply copy and paste the content into the input. So it would not filter the comma character.
You should use the onChange event and add a Regex to test if the input is valid.
const Input = ({ placeholder }) => {
const [inputValue, setInputValue] = useState("");
const handleChange = e => {
const filteredInput = e.target.value.replace(/[^\w\s]/gi, "");
setInputValue(filteredInput);
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={handleChange}
placeholder={placeholder}
/>
</div>
);
};
But how it works...
So the regex is currently allowing any word, digit (alphanumeric) and whitespaces. You could for example extend the whitelist to allow # by doing const filteredInput = e.target.value.replace(/[^\w\s#]/gi, ""); Any rule inside the [^] is allowed. You can do some regex testing here https://regexr.com/55rke
Also you can test my example at: https://codesandbox.io/s/nice-paper-40dou?file=/src/App.js

Redux-form's Field component is not triggering normalize function if Field's type is number

I'm passing antd's component (FormFieldInput) to redux-form's Field component. Everything works well with "text" and "telephone" input types. Normalize number function stops working as soon as I change Field's type to "number".
I can see that FormFieldInput component is triggered only when I input numbers. When I'm typing alphabetic characters into the input FormFieldInput console log at the top of the function is not returning new values.
Normalizer:
const normalizeNumber = (value /* , previousValue */) => {
console.log('---input', value);
if (!value) {
return value;
}
const onlyNums = value.replace(/[^\d]/g, '');
console.log('---output', onlyNums);
return onlyNums;
};
Usage:
<Field
name="size"
type="number"
component={FormFieldInput}
label="Size"
placeholder="Size"
required
validate={[required, maxLength255]}
normalize={normalizeNumber}
/>
FormFieldInput:
const FormFieldInput = ({
input,
label,
type,
placeholder,
required,
meta: { touched, error, warning }
}) => {
const [hasError, setHasError] = useState(false);
const [hasWarning, setHasWarning] = useState(false);
console.log('---input', input);
useEffect(() => {
setHasError(!!error);
}, [error]);
useEffect(() => {
setHasWarning(!!warning);
}, [warning]);
const ref = createRef();
return (
<div className="form-item">
<div className="form-item__label">
{`${label}:`}
{required && <span style={{ color: 'red' }}> *</span>}
</div>
<div className={`form-item__input`}>
<AntInput
{...input}
ref={ref}
placeholder={placeholder}
type={type}
/>
</div>
</div>
);
};
export const AntInput = React.forwardRef((props, ref) => {
const { placeholder, type, suffix } = props;
console.log('---type', type);
return <Input {...props} ref={ref} placeholder={placeholder} type={type} suffix={suffix} />;
});
I expect all input data to go through normalize function, but somehow alphabetic characters are going through it.

Categories

Resources