I am trying to make a validation in reactjs.
For example, I have a field called "name" and every time you input a name, this has to be bigger or equal to 2. It works when the name is smaller than 2, but if it's bigger than 2 the message still appears.
Here is my code:
const [data, setData] = React.useState({
nameValid: true,
prenumeValid: true,
check_textInputChange: false,
name: "",
});
const handleValidUser = (val) => {
if (val.length >= 2) {
setData({
...data,
nameValid: true,
});
} else {
setData({
...data,
nameValid: false,
});
}
};
const textInputChange = (val) => {
if (val.length >= 2) {
setData({
...data,
name: val,
nameValid: true,
check_textInputChange: true,
});
} else {
setData({
...data,
name: val,
nameValid: false,
check_textInputChange: false,
});
}
};
<TextField
type="text"
variant="outlined"
label="Nume"
required
fullWidth
autofocus
validate
style={{ marginBottom: "1em" }}
onChange={(val) => textInputChange(val)}
onBlur={(e) => handleValidUser(e)}
/>
{data.nameValid ? null : (
<text>The name has to be at least 2 letters long</text>
)}
onChange has an event parameter, if you want to get the value of the TextField, you should write it as below.
const textInputChange = e => {
const val = e.target.value;
if (val.length > 2) {
setData(...)
} else {
setData(...)
}
}
return (
<TextField
...
onChange={textInputChange}
onBlur={handleValidUser} // as well as onBlur
/>
)
I've created a codesandbox to demonstrate here.
Call the function as below will create an extra render every time the function is called. You should avoid it.
onChange={(val) => textInputChange(val)}
Can you try changing
onChange={(val) => textInputChange(val)}
to this:
onChange={(e) => textInputChange(e.target.value)}
in function textInputChange you are passing event object instead of input value
you have to change onchange handler from:
onChange={(val) => textInputChange(val)}
to
onChange={(val) => textInputChange(val.target.value)}
Related
const [formData, setFormData] = useState({});
useEffect(() => {
fetch("/formdata")
.then((res) => res.json())
.then((data) => setFormData(data));
}, []);
console.log("Form Data", formData);
//Sorting by order
let attr;
form.forms.map((y) => {
return (attr = y.formAttributes.sort((a, b) => {
return a.order < b.order ? -1 : 1;
}));
});
return (
{attr.map((attri, index) => {
return (
<TextField
key={index}
label={attri.label}
value={formData[attri.datakey] || ""}
onChange={event => {const {value} = event.target; setFormData({formData: value})}}
/>
);
})}
)
I would like to ask help on how to manage multiple fields in Textfield onChange area? Currently, if I am going to input a value there are no changes that is happening.
Here's my code.
Tried the approach of using e.target.value however it would still stay the same.
You should add a name attribute, where the 'name' is the key of the key-value-pair in your object.
e.g.
<TextField
key={index}
name="somename"
label={attri.label}
value={formData[attri.datakey] || ""}
onChange={handleChange}
/>
Then you can update the field like that.
setFormData({
...formData,
[e.target.name]: e.target.value // becomes "somename: some value"
})
Background:
I am successfully able to use separate states/handlers to populate and edit my text fields, but I would like to simplify/condense my code by creating a unified state and a unified handler for many of my text fields.
Problem:
I am successfully able to obtain the correct values with useEffect, but when I try to edit one of the text fields I get an error message saying: TypeError: Cannot read property 'value' of undefined
My Code:
useEffect(() => {
getDataFromApi(props.id).then((rsp) => {
setData(rsp);
textFields.firstItem = rsp.content?.firstItem;
textFields.secondItem = rsp.content?.secondItem;
});
}, []);
const [textFields, setTextFields] = useState({
firstItem: "",
secondItem: "",
});
const handleChangeTextField = (type) => (e) => {
setTextFields((prevState) => ({
...prevState,
[type]: e.target.value,
}));
};
<TextField
id="firstItem"
value={textFields.firstItem}
onChange={handleChangeTextField("firstItem")}
/>
<TextField
id="secondItem"
value={textFields.secondItem}
onChange={handleChangeTextField("secondItem")}
/>
Any suggestions on what I am doing wrong would be helpful!
Thanks.
If you use that try like this.
const [textFields, setTextFields] = useState({
firstItem: "",
secondItem: ""
});
const handleChangeTextField = (type) => (value) => {
setTextFields((prevState) => ({
...prevState,
[type]: value
}));
};
<TextField
id="firstItem"
variant="filled"
value={textFields.firstItem}
onChange={handleChangeTextField("firstItem")}
/>
<TextField
id="secondItem"
value={textFields.secondItem}
variant="filled"
onChange={handleChangeTextField("secondItem")}
/>
You can confirm here. https://codesandbox.io/s/wizardly-davinci-3x0ui?file=/App.js:0-865
I'm new to the react-hook-form world. I'm trying my best to understand but there are things that I'm still missing. I need an Obiwan help!
Here is the problem :
I have an input number which is render using the <Textfield> Material-ui component inside react-hook-form <Controller/ component. On this input I need to have a validation that the number enter sould be between 16 and 99. With Material-UI you can pass the param inputProps={{min:16, max:99}} to block the number on the native input! Great!
But what about if is a user manually enter a value ? Then I want to be able to block it as well in an onBlur validation method, here is what I did:
<TextField
onBlur={(e) =>{
const currentValue = Number(e.target.value);
if (!(currentValue >= minAge && currentValue <= maxAge)) {
e.target.value = String(minAge);
ref.current.value = String(minAge);
}
onBlur();
console.log('TextField onBlur(): ', e.target.value);
}}
inputRef={ref}
inputProps={{
min:minAge,
max:maxAge
}}
/>
However when I log the value after a blur it tells me that nothing has change. Please help.
Here is the full file :
import React, {useEffect} from "react";
import { TextField } from "#material-ui/core";
import { Controller, useFormContext } from "react-hook-form";
import { ErrorMessage } from "#hookform/error-message";
import { ErrorMessageContainer } from "./ErrorMessageContainer";
import { FieldWrapper } from "./FieldWrapper";
interface AgeTextboxProps {
name: string;
label?: string;
}
export const AgeTextbox = ({ name, label }: AgeTextboxProps) => {
const { errors, control } = useFormContext();
const minAge = 16;
const maxAge = 99
return (
<FieldWrapper caption={label}>
<Controller
name={name}
control={control}
rules={{
required: { value: true, message: `You must enter the ${label} age` },
min: { value: minAge, message: `The ${label} age cannot be lower than 16` },
max: { value: maxAge, message: `The ${label} age cannot be higher than 99` }
}}
render={({ onChange, onBlur, value, ref }) => (
<TextField
onChange={(e) => onChange(e.target.value)}
onBlur={(e) =>{
const currentValue = Number(e.target.value);
if (!(currentValue >= minAge && currentValue <= maxAge)) {
e.target.value = String(minAge);
ref.current.value = String(minAge);
}
onBlur();
console.log('TextField onBlur(): ', e.target.value);
}}
onFocus={(event) => {
event.target.select();
}}
inputRef={ref}
value={value || 0}
error={errors[name] ? true: false}
role={"textbox"}
variant="outlined"
type={"number"}
inputProps={{
min:minAge,
max:maxAge
}}
/>
)}
/>
<ErrorMessage
errors={errors}
name={name}
as={<ErrorMessageContainer />}
/>
</FieldWrapper>
);
};
export default AgeTextbox;
Here is a codesandbox link also : https://codesandbox.io/s/eloquent-ishizaka-980qb?file=/src/AgeTextBox.tsx
Here is what I did for you. May be you wanted this behavior.
I just called onChange inside onBlur
onBlur={(e) => {
const currentValue = Number(e.target.value);
if (currentValue < minAge || currentValue > maxAge) {
onChange(String(minAge));
} else {
onChange(e.target.value);
}
onBlur();
console.log("TextField onBlur(): ", e.target.value);
}}
https://codesandbox.io/s/modest-archimedes-6c1nl?file=/src/AgeTextBox.tsx
Full example on CodeSandbox
(Css is a bit borked)
Writing anything into the input field or the textarea and then clicking on the select wipes the input field & the textarea, I am not sure why -
It seems that is because I am passing jsx elements to the HoverWrapper element.
When I just inlined the WrapInHover element it behaved as expected. Am I passing Elements in a bad way ?
Adding a key to the passed elements didn't seem to solve the issue ...
const Form = () => {
const selectInit = {
open: false,
initial: true,
selected: 'please select',
};
const selectReducer = (state, action) => {
switch (action.type) {
case 'toggle': {
return { ...state, open: !state.open };
}
case 'select': {
return { ...state, selected: action.selected, open: !state.open, initial: false };
}
}
};
const [selectState, selectDispatch] = useReducer(selectReducer, selectInit);
const selectHelp = selected => selectDispatch({ type: 'select', selected });
const OptionComp = ({ txt, value, onClick }) => (
<Option onClick={onClick} state={selectState} value={value}>
{selectState.open && selectState.selected === value ? null : <HoverBorder />}
{txt}
</Option>
);
const WrapInHover = ({ elements }) => {
const [hover, setHover] = useState(false);
return (
<div
css={css`
position: relative;
`}
onMouseEnter={() => {
setHover(true);
}}
onMouseLeave={() => {
setHover(false);
}}>
{elements}
<HoverBorder hover={hover} />
</div>
);
};
return (
<FormEl>
<WrapInHover elements={<Input key='ContactEmailInput' type='email' required />} />
<Label htmlFor='subject' onClick={() => selectDispatch({ type: 'toggle' })}>
Subject
</Label>
<Select>
<OptionComp
onClick={() => selectHelp('art')}
txt='I want you to paint something !'
value='art'
/>
{selectState.initial && !selectState.open ? (
<OptionComp
txt='Please Select An Option'
value='please select'
onClick={() => selectDispatch({ type: 'toggle' })}
/>
) : null}
</Select>
</FormEl>
);
};
Store value of input and message inside state. Also input will lose focus if your WrapInHover is inside main function
export default function App() {
const Form = () => {
const [formState, setFormState] = useState({ email: "", message: "" });
...
const handleFormDataChange = (e, type) => {
const {target: { value }} = e;
setFormState((prevState) => ({ ...prevState, [type]: value }));
};
return (
<FormEl>
<FormTitle>Contact me</FormTitle>
<Label htmlFor="email">Email</Label>
<WrapInHover
elements={
<Input
key="ContactEmailInput"
type="email"
value={formState.email}
onChange={(e) => handleFormDataChange(e, "email")}
required
/>
}
/>
...
<Label htmlFor="message">Message</Label>
<WrapInHover
elements={
<TextArea
key="ContactMessageTextArea"
name="message"
value={formState.message}
onChange={(e) => handleFormDataChange(e, "message")}
/>
}
/>
CSB Example - I will delete after 24 hours.
This is my react-numpad widget I created:
const state = {
number: 0
};
//MAYBE the three following lines are wrong. How to use handleChhange in react-numpad?
const handleChange = e => {
this.setState(e.target.value);
};
const handleBlur = e => {
if (e.target.value === "0") e.target.value = "0";
};
const handleKeypress = e => {
const characterCode = e.key;
if (characterCode === "Backspace") return;
const characterNumber = Number(characterCode);
if (characterNumber < 0) {
e.preventDefault();
}
};
const myTheme = {
fontFamily: "Arial",
textAlign: "center",
header: {
primaryColor: "#263238",
secondaryColor: "#f9f9f9",
highlightColor: "#FFC107",
backgroundColor: "#607D8B"
},
body: {
primaryColor: "#263238",
secondaryColor: "#32a5f2",
highlightColor: "#FFC107",
backgroundColor: "#f9f9f9"
},
panel: {
backgroundColor: "#CFD8DC"
}
};
const priceWidget = ({
step,
precision,
input,
placeholder,
label,
theme,
props,
meta: { touched, error },
...rest
}) => {
return (
<div className="form-group">
<label forname={input.name}>{label}</label> <br />
<NumPad.Number
{...rest}
step={0.1}
precision={2}
placeholder={!input.value ? "please, type a number" : input.value}
selected={input.value ? new NumPad.Number(input.value) : null}
onKeyDown={changedVal => handleKeypress(changedVal)}
onBlur={changedVal => handleBlur(changedVal)}
//IN the FOLLOWING LINE it doesn't work
onChange={value => handleChange(value)}
//BUT IN THIS ONE FOLLOWING LINE WORKS FINE but the field doesn't get the setted value
// onChange={(value) => console.log("price's value",value)}
//ISSUE theme= {myTheme}
//ISSUE onChange={input.onChange}
//ISSUE onBlur={input.onBlur}
className="form-control"
/>
<div className="text-danger" style={{ marginBottom: "20px" }}>
{touched && error}
</div>
</div>
);
};
export default priceWidget;
When I'm executing this line:
onChange={(value) => handleChange(value)}
I've got following issue:
TypeError: Cannot read property 'setState' of undefined
But when I'm executing this another line:
onChange={(value) => console.log("price's value",value)}
It works fine but the field doesn't update with value I selected from my numpad.
e.g. I selected 44 when I'm executing this line:
onChange={(value) => console.log("price's value",value)}
and shows value on console but the field from numpad doesn't update nor show the selected value. It's empty.
So, I have to use handleChange method to change input value but it's not well- implemented.
How can I solve this issue?, anyone knows how can I fix this issue?
I see that App component is a functional component. So this.setState way of updating the state will not work. You will have to use useState hook.
For more information - https://reactjs.org/docs/hooks-state.html