I'm trying to prevent my TextInput from getting values like $,%,^,&,(,) etc. Basically my TextInput should allow letters only. My approach is as follows. But still i'm able to input these other characters. How can i prevent special characters from the TextInput
restrict(event) {
const regex = new RegExp("^[a-zA-Z]+$");
const key = String.fromCharCode(!event.charCode ? event.which : event.charCode);
if (!regex.test(key)) {
event.preventDefault();
return false;
}
}
<TextInput
underlineColorAndroid='transparent'
allowFontScaling={false}
style={styles.questionText}
onKeyPress={e => this.restrict(e)}
value={firstNameState}
/>
the onKeyPress event on android does not work very well.
That is why I have chosen to use a method that eliminates these characters and then save it wherever you want, just as it might change the state of your field.
restrict = text => text.replace(/[`~0-9!##$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '')
I have to block special characters by this line of code.
var format = /[!##$%^&*()_+-=[]{};':"\|,.<>/?]+/;
if(format.test(string)){ }
You may define your OnChange event handler using your regex, where you will check if the input string matches your regex with /^[^!-\/:-#\[-`{-~]+$/.test(text):
const {useState} = React;
const App = () => {
const [value, setValue] = useState("");
const onChange = e => {
const input = e.currentTarget.value;
if (/^[^!-\/:-#\[-`{-~]+$/.test(input) || input === "") {
setValue(input);
}
};
return (
<div className="App">
<input
value={value}
onChange={onChange}
underlineColorAndroid='transparent'
allowFontScaling={false}
/>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Related
I have a form. In the form I getting brandName, supplierName, and date of expiry from the user.
I am usinf TextField from mui library and submit button.
I want to disable submit button on empty form fields and enable it when user filled all the inputs
here is my code for declaring useStates
const[brandName, setBrandName] = useState("");
const[supplierName, setSupplierName] = useState("");
const[expiryDate, setExpiryDate] = useState(null);
const[brandNameError, setBrandNameError] = useState(false);
const[supplierNameError, setSupplierNameError] = useState(false);
const[expiryDateError, setExpiryDateError] = useState(false);
const[submitButton, setSubmitButton] = useState(true);
and here is all function which i used to validate my inputs
// checking brandName Error
const brandNameValidateOnBlur = ()=>{
if(brandName === ""){
setBrandNameError(true);
}
}
// checking supplier name error
const supplierNameValidateOnBlur = ()=>{
if(supplierName === ""){
setSupplierNameError(true);
}
}
// checking expiry date
const expiryDateValidateOnBlur = ()=>{
if(expiryDate === ""){
setExpiryDateError(true);
}
}
// now checking all inputs again if all inputs are good then
// button should be enabled
const checkAllInputs = ()=>{
if(brandName !== "" && supplierName !== "" && expiryDate !== ""){
setSubmitButton(false);
}else{
setSubmitButton(true);
}
}
and here is the rest of code
<TextField fullWidth id="productName" label="Product Name"
value={brandName.toLowerCase()} variant="outlined"
onChange={(data)=>{setBrandName(data.target.value.toUpperCase());checkAllInputs()}}
onBlur={brandNameValidateOnBlur}
onFocus={()=>setBrandNameError(false)}
error={brandNameError}
helperText = {brandNameError ? "Enter Brand Name" : ""}
/>
<TextField id="supplierName"
label="Supplier Name"
value={supplierName.toLowerCase()} variant="outlined"
onBlur={supplierNameValidateOnBlur}
onFocus={()=>setSupplierNameError(false)}
error={supplierNameError}
helperText={supplierNameError ? "Enter Supplier Name " : ""}
onChange={(data)=>{setSupplierName(data.target.value.toUpperCase());checkAllInputs()}} />
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DesktopDatePicker
label="Date Expiry"
inputFormat="MM/DD/YYYY"
value={expiryDate}
onBlur={expiryDateValidateOnBlur}
onFocus={()=>setExpiryDateError(false)}
error={expiryDateError}
helperText={expiryDateError ? "Enter Expiry Date ": ""}
onChange={(selectedDate)=>
{setExpiryDate(selectedDate.format("MM/DD/YYYY"));checkAllInputs()}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<Button type='button' variant='contained' id="submitButton" disabled={submitButton}
style={{backgroundColor:'orangered'}} onClick={addData}>Add Data</Button>
now problem is when i enter a single word my useState is updated but my checkAllInputs not working as i want to be like if i enter a word my checkAllInputs method runs before setting of state as i enter second entry then it works like I want to be so i did not know what i am doing wrong
I TRY useEffect hook like that
useEffect(()=>{
checkAllInputs();
},[brandName, supplierName, expiryDate]);
its working fine as i accepted but I read that it should be a expensive to use useEffect
and an other approach which i use
const checkAllInputsWithDom = ()=>{
let brandNameEntry = document.getElementById("brandName").value;
let supplierNameEntry = document.getElementById("supplierName").value;
let expiryDateEntry = document.getElementById("expiryDate").value;
if(brandNameEntry !=="" && supplierNameEntry !== "" && expiryDateEntry !== ""){
setSubmitButton(false);
}else{
setSubmitButton(true);
}
}
it is working out of box but I thing in react this is against react principles to direct manipulate
dome elements can I do it with out using useeffect hook like only with my metohd and one more thing
this did not set expiryDate error in date field
As long as you're keeping the input values in state, you don't need another variable storing the button state as well. You can calculate it at render time. (Read more about avoiding redundant state here.)
You could do something like this:
const Component = () => {
const[brandName, setBrandName] = useState("");
const[supplierName, setSupplierName] = useState("");
const[expiryDate, setExpiryDate] = useState("");
const disabled = brandName.length === 0 && supplierName.length === 0 && expiryDate.length === 0
return (
<>
<input value={brandName} onChange={(e) => setBrandName(e.target.value)}/>
<input value={supplierName} onChange={(e) => setSupplierName(e.target.value)}/>
<input value={expiryDate} onChange={(e) => setExpiryDate(e.target.value)}/>
<button disabled={disabled}>Button</button>
</>
)
}
here is the link for codesandbox I have a suggestion for you to refactor this 6 use States with just one useState as an object having all the values like the following:
const initialValues = {
brandName: "",
supplierName: "",
expiryDate: "",
brandNameError: "",
supplierNameError: "",
expiryDateError: ""
};
and then update it with a generic handleInputChange like below:
const handleInputChange = (e) => {
//const name = e.target.name
//const value = e.target.value
const { name, value } = e.target;
setValues({
...values,
[name]: value
});
let isEmpty = Object.values(values).some((x) => x === "");
console.log(isEmpty);
setIsDisabled(isEmpty);
};
FOR YOUR SOLUTION
You will have to just check all the values and just create a boolean with isDisabled so when all of those values are empty then it will only set to false.
I have created a code snippet for you, it needs some improvement but it will serve your purpose. Link already at the top and here as well
as suggested by Eduardo Motta de Moraes
I used this for making my button enabled after all inputs validate
const disabled= brandName.length === 0 || supplierName.length === 0 || expiryDate.length === 0;
this solve my problem
thanks for Eduardo Motta de Moraes for this
I have an input where users are going to type identification numbers and I would like to mask that input so that it always has this format : XX-XXXXXXXX-X
The X's can only be numbers and the dashes need to be always in those positions.
Here is what I got so far:
import React from "react";
import { Inertia } from "#inertiajs/inertia";
import {useForm, usePage} from "#inertiajs/inertia-react";
import ErrorForm from "./ErrorForm"
function Login() {
const{data , setData , processing ,reset} = useForm({
cuit: '',
password: ''
})
const errors = usePage().props.errors
function submit(e){
e.preventDefault()
Inertia.post(route('login'),data,{
onError:() => reset('password')
})
}
function handleChange(e){
if(e.target.value.length === 11){
e.target.value = [e.target.value.slice(0,11),'-'].join('')
}else if(e.target.value.length >= 2){
if(!e.target.value.includes('-')){
e.target.value = [e.target.value.slice(0,2),'-',e.target.value.slice(2)].join('')
}
}
setData('cuit',e.target.value)
}
function handleKeyDown(e){
if(e.key === "0" || e.key === "1" || e.key === "2" || e.key === "3" || e.key === "4" || e.key === "5" || e.key === "6" || e.key === "7" || e.key === "8" || e.key === "9"){
handleChange(e)
}
}
return(
<div className="ContenedorLogin">
<form onSubmit={submit}>
<input
name="cuit"
type="text"
placeholder="C.U.I.T."
className="input"
onKeyDown={handleKeyDown}
maxLength="13"
/>
{errors.cuit &&
<ErrorForm
content={errors.cuit}
/>
}
<input
name="password"
type="Password"
placeholder="Contraseña"
className="input"
value={data.password}
onChange={e => setData('password',e.target.value)}
/>
<button className="btn-consejo" type="submit" disabled={processing}>INGRESAR</button>
</form>
</div>
)
}
export default Login
Basically I'm capturing a keyDown event, then changing the input only if the user typed a number and finally in the handleChange function I try to mask and set the value.
It kind of work but not for all cases, for example if I'm in the middle of typing and I already have the first dash and I add a number before the first dash its going to allow it leaving me with something like this : XXXX-XXXXX
I imagine I can achieve the result using regular expressions or something like that but I'm not familiar at all with them
Thanks in advance!
It would work if you bind event on whole input value instead of each key press.
Here, instead of using event "onKeyDown", use "onChange". Also You can use html pattern for accepting number only.
<input
name="cuit"
type="number"
pattern="[0-9\/]*"
placeholder="C.U.I.T."
className="input"
onChange={handleChange} // calling handleChange on input change directly
maxLength="13"
/>
You already have working handleChange() function, which will work perfectly.
Instead of using JS to do this use the in-build HTML form validation.
Add a required attribute and a regex pattern to the input. The form won't submit until the input has been validated. And you'll also get little tool-tips to explain what the issue is if you try to submit and the input isn't validated.
In this case the regex reads:
^ - start of the value
[0-9]{2}- two numbers followed by a dash
[0-9]{8}- eight numbers followed by a dash
[0-9] - one number
$ - end of the value
const { useState } = React;
function Example() {
const [input, setInput] = useState('');
function handleSubmit(e) {
e.preventDefault();
console.log(`Submitted: ${input}`);
}
function handleChange(e) {
setInput(e.target.value);
}
return (
<form onSubmit={handleSubmit}>
<input
onChange={handleChange}
placeholder="dd-dddddddd-d"
pattern="^[0-9]{2}-[0-9]{8}-[0-9]$"
value={input}
required
/>
<button type="submit">Submit</button>
</form>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
input, button { font-size: 1.2em; }
input { padding: 0.3em; }
input:invalid { border: 1px solid red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I don't want any space before the input and after the input like " text" and "text " does not allow so I replace the white space but when we copy "text " from notepad and paste over the input and want to remove the space it throws error like "can not read property of undefined reading target".so how to do like when user give space front and back its automatically replace whitespace
const handleKeyDown = (e) => {
if (e.key === " ") {
e.preventDefault();
}
};
const handleChangeWhiteSpace = (e) => {
if (e.target.value.includes(" ")) {
e.target.value = e.target.value.replace(/\s/g, "");
}
};
<MyInput
type="text" style={{width:'240px'}}
error={formik.errors.input && formik.touched.input}
value={formik.values.input}
onBlur={formik.handleBlur}
onChange={(e)=>{formik.handleChange(e);handleChangeWhiteSpace()}}
onKeyDown={handleKeyDown}
name="input"
id="input"
autoFocus={false}
autoComplete="off"
/>
using regex the following should work, you can test it at regex101:
e.target.value = e.target.value.replace(/^[ \t]+|[ \t]+$/gm, "");
the cleaner solution would be what sojin suggested
e.target.value = e.target.value.trim()
Replace
const handleChangeWhiteSpace = (e) => {
if (e.target.value.includes(" ")) {
e.target.value = e.target.value.replace(/\s/g, "");
}
};
With this
const handleChangeWhiteSpace = (e) => {
e.target.value = e.clipboardData.getData('Text').trim();
};
To register changes when you paste text inside the text field use the onPaste event
onPaste={handleChangeWhiteSpace}
Final Code
const handleKeyDown = (e) => {
if (e.key === " ") {
e.preventDefault();
}
};
const handleChangeWhiteSpace = (e) => {
e.target.value = e.target.value.trim();
};
<MyInput
type="text" style={{width:'240px'}}
error={formik.errors.input && formik.touched.input}
value={formik.values.input}
onBlur={formik.handleBlur}
onPaste={handleChangeWhiteSpace}
onChange={(e)=>{formik.handleChange(e);
handleChangeWhiteSpace()}}
onKeyDown={handleKeyDown}
name="input"
id="input"
autoFocus={false}
autoComplete="off"
/>
use normalize={(value, prevVal, prevVals) => value.trimStart()} after rules{[]} in form.item
for prevent whitespace before value in antd input
for example check selected code in image ==>
enter image description here
To dont allow to send only spaces you can use normalize prop
<MyInput
type="text" style={{width:'240px'}}
normalize={(value) => value.trimStart()}
error={formik.errors.input && formik.touched.input}
value={formik.values.input}
and then you can remove all of the spaces inside of this function
const onFinish = (values) => {
//values.input1=values.input1.trim() or whatever
}
btw this function you can use to remove all the spaces
const remove = (str) => str.trim().replace(/\s\s+/g, ' ')
I'm trying to make a input box component that has instant feedback using Formik. I want the input box to turn green when the user input matches a predefined string (the "answer"), gray if the input matches the prefix of the answer (including the empty string) and red otherwise. This string is stored as a property of the initial values, values.answer. The Formik validate function checks if the input equals values.answer and sets values.correct = true. I then created a css class corresponding to a green input box and set the className of the input conditional on the value of values.correct. The problem is it only seems to update (i.e turn green with a correct input) when I click out of focus of the input box (i.e onBlur). I would like it to work onChange. How would I do this?
Here is the relevant code sandbox: https://codesandbox.io/s/instant-feedback-box-lub0g?file=/src/Frame.js
Cool problem, but you've overcomplicated your code a little bit 😉 Some feedback:
touched is set to true during onBlur by default. You can override this by using setTouched(), but I found it simpler to just use values instead of touched in your form
try to keep values as minimal as possible, it's only meant to access input values so there's no need for hint and answer to be assigned to it
the purpose of the validation function is to return an errors object and not to set values, so remove assignments like values.correct = true
You don't need to store isDisabled in state, you can derive it from formik.submitCount and formik.isSubmitting
const Note = () => {
const [showFrame, setShowFrame] = useState({ 1: true });
const onCorrectSubmission = (frameId) => {
setShowFrame({ ...showFrame, [frameId]: true });
};
const text =
"What is the sum of the first three natural numbers? (give answer as a word, i.e one, two etc.)";
const hint = "The first three natural numbers are 1, 2, and 3";
const answer = "six";
return (
<div>
<h1>Induction</h1>
{showFrame[1] ? (
<Frame
id={1}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
{showFrame[2] ? (
<Frame
id={2}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
</div>
);
};
const Frame = ({
id,
text,
hint,
answer,
values,
onCorrectSubmission,
...props
}) => {
const validate = (values) => {
const errors = {};
if (!answer.startsWith(values.cloze)) {
errors.cloze = hint;
} else if (values.cloze !== answer) {
errors.cloze = true;
}
return errors;
};
const formik = useFormik({
initialValues: {
cloze: ""
},
validate,
onSubmit: (values) => {
onCorrectSubmission(id + 1);
}
});
const isFinished = formik.isSubmitting || formik.submitCount > 0;
return (
<form enablereinitialize={true} onSubmit={formik.handleSubmit}>
<p>{text}</p>
<input
id="cloze"
name="cloze"
type="text"
autoComplete="off"
{...formik.getFieldProps("cloze")}
disabled={isFinished}
className={`input
${!answer.startsWith(formik.values.cloze) ? "invalid-input" : ""}
${formik.values.cloze && !formik.errors.cloze ? "valid-input" : ""}
`}
/>
{formik.values.cloze && formik.errors.cloze ? (
<div>{formik.errors.cloze}</div>
) : null}
<button disabled={!!formik.errors.cloze || isFinished} type="submit">
Submit
</button>
</form>
);
};
export default Frame;
Live Demo
I would like to add on a input a thousand separator using React Hooks but I'm not sure how. I have tried the below code so far and is not working.
Can you please point out what could be the issue and how can I implement this?
Thank you.
const MainComponent = () => {
const [value, setValue] = useState(0);
const numberWithComma = () => {
return (+value).toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')
}
return (
<div>
<input
type="number"
onChange={numberWithComma}
placeholder="0"
/>
</div>
);
}
You want a controlled form input, so one which gets given a value, and an onInput handler.
You also need it to be a type="text" to allow for the commas to be added, or Chrome will not allow you to set that. However, then to prevent non-numeric chars being added you need another function to strip them out before setting the value.
See the below working snippet:
const {useState} = React;
const MainComponent = () => {
const [value, setValue] = useState(0);
const addCommas = num => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
const removeNonNumeric = num => num.toString().replace(/[^0-9]/g, "");
const handleChange = event =>
setValue(addCommas(removeNonNumeric(event.target.value)));
return (
<div>
<input type="text" value={value} onInput={handleChange} />
</div>
);
};
// Render it
ReactDOM.render(
<MainComponent/>,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>