I am facing an issue when I try to set the state of a component in the Input field. I have tried by setting the Object values in the constructor but that didn't work either. If I remove the prop the input field is not getting any value. I have posted my JS code below.
Input Component:
<div className="form__group">
<input
className="form__field"
id={props.name}
name={props.name}
type={props.inputtype}
value={props.value}
onChange={props.handleChange}
placeholder={props.placeholder}
{...props}
/>
<label htmlFor={props.name} className="form__label">{props.placeholder}</label>
</div>
JSX Code:
<Input
inputtype={"text"}
title={"Full Name : "}
name={"name"}
value={this.state.newUser.name}
handleChange={this.handleInput}
placeholder={"Enter your Name"}
/>{" "}
JS Code:
class FormContainer extends Component {
constructor(props) {
super(props);
this.state = {
newUser: {
name: "",
age: "",
gender: "",
skills: [],
about: ""
},
this.handleInput = this.handleInput.bind(this);
}
handleInput(e) {
let value = e.target.value;
let name = e.target.name;
this.setState(
prevState => ({
newUser: {
...prevState.newUser,
[name]: value
}
}),
() => console.log(this.state.newUser)
);
}
}
handleChange is passed on because of {...props}, and is not a valid for the input element.
If you spread out the props in the parameters:
const Input = ({ name, inputType, value, handleChange, placeholder, ...props}) => {
and then
<input
className="form__field"
id={name}
name={name}
type={inputType}
value={value}
onChange={handleChange}
placeholder={placeholder}
{...props}
/>
Actually there is a problem in your JSX (handleChange instead of onChange, but it is not the main thing)
So in my example your final code should look like this
handleInput = (input) => {
this.setState({ newUser: { [input.target.name]: input.target.value } });
}
<Input
inputtype="text"
title="Full Name : "
name="name"
value={this.state.newUser.name}
onChange={this.handleInput}
placeholder="Enter your Name"
/>
Related
I'm new to React and I implemented a form which uses Material-UI and react-input-mask library. I would like to know how to change user input value in the input field.
For example, I have this field:
class TemplateForm extends React.Component {
constructor(props) {
super(props);
this.state= {
api_access: true,
currency: "",
sku: "",
}
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange = (event) => {
const {name, value} = event.target
this.setState({
[name]: value
})
}
onSubmit = (formVals) => {
return false;
}
renderField=({input,label, meta:{touched,error}})=> (
<InputMask mask="99/9999"
value = {this.state.edate}
name="edate"
type="tel"
required
{...input}
>
{(inputProps) =>
<TextField
{...inputProps}
label={label}
fullWidth
/>}
</InputMask>
)
handleBlur = (value) => {
valueM = value.split("/")
if (valueM[0].length< 2) {
valueM[0]= "0"+valueM[0]
}
valueM = valueM.join("/")
this.setState({
ename:valueM
})
}
<Field id="ename" name="ename" onBlur={this.handleBlur} component={this.renderField} />
}
I would like to change value of the month field with a leading zero when user enters only one digit. Like if user enters 1_/2022 should be converted to 01/2022 but this.setState() isn't changing the value in the field. How to achieve this?
I am having a simple form that has firstName and lastName.
<label htmlFor="firstName">First Name: </label>
<input
type="text"
className="form-control"
id="firstName"
name="firstName"
value={basicDetails.firstName}
onChange={(event) => handleInputChange(event)}
/>
<label htmlFor="lastName">Last Name: </label>
<input
type="text"
className="form-control"
id="lastName"
name="lastName"
value={basicDetails.lastName}
onChange={(event) => handleInputChange(event)}
/>
For this I am trying to add validation.
The validation rules are,
Both fields should accept only text
First name is required and should have at least 4 characters.
If Last name field has value, then it needs to be at least 3 characters.
Things I have tried to achieve this,
components/utils.js
export function isLettersOnly(string) {
return /^[a-zA-Z]+$/.test(string);
}
components/basic_details.js
const handleInputChange = (event) => {
const { name, value } = event.target;
if (!isLettersOnly(value)) {
return;
}
setValue((prev) => {
const basicDetails = { ...prev.basicDetails, [name]: value };
return { ...prev, basicDetails };
});
};
On handle input field, I am making the validation to check whether the input has value but I am unable to get the point how to catch the actual validation error and display below respective input box.
Kindly please help me to display the validation message on the respective fields.
Working example:
I suggest adding an errors property to the form data in form_context:
const [formValue, setFormValue] = useState({
basicDetails: {
firstName: '',
lastName: '',
profileSummary: '',
errors: {},
},
...
});
Add the validation to basic_details subform:
const ErrorText = ({ children }) => (
<div style={{ color: 'red' }}>{children}</div>
);
const BasicDetails = () => {
const [value, setValue] = React.useContext(FormContext);
const { basicDetails } = value;
const handleInputChange = (event) => {
const { name, value } = event.target;
if (!isLettersOnly(value)) {
setValue((value) => ({
...value,
basicDetails: {
...value.basicDetails,
errors: {
...value.basicDetails.errors,
[name]: 'Can have only letters.',
},
},
}));
return;
}
switch (name) {
case 'firstName': {
const error = value.length < 4 ? 'Length must be at least 4.' : null;
setValue((value) => ({
...value,
basicDetails: {
...value.basicDetails,
errors: {
...value.basicDetails.errors,
[name]: error,
},
},
}));
break;
}
case 'lastName': {
const error = value.length < 3 ? 'Length must be at least 3.' : null;
setValue((value) => ({
...value,
basicDetails: {
...value.basicDetails,
errors: {
...value.basicDetails.errors,
[name]: error,
},
},
}));
break;
}
default:
// ignore
}
setValue((prev) => {
const basicDetails = { ...prev.basicDetails, [name]: value };
return { ...prev, basicDetails };
});
};
return (
<>
<br />
<br />
<div className="form-group col-sm-6">
<label htmlFor="firstName">First Name: </label>
<input
type="text"
className="form-control"
id="firstName"
name="firstName"
value={basicDetails.firstName}
onChange={(event) => handleInputChange(event)}
/>
</div>
<br />
{basicDetails.errors.firstName && (
<ErrorText>{basicDetails.errors.firstName}</ErrorText>
)}
<br />
<br />
<div className="form-group col-sm-4">
<label htmlFor="lastName">Last Name: </label>
<input
type="text"
className="form-control"
id="lastName"
name="lastName"
value={basicDetails.lastName}
onChange={(event) => handleInputChange(event)}
/>
</div>
<br />
{basicDetails.errors.lastName && (
<ErrorText>{basicDetails.errors.lastName}</ErrorText>
)}
<br />
</>
);
};
Lastly, check the field values and errors to set the disabled attribute on the next button in index.js. The first !(value.basicDetails.firstName && value.basicDetails.lastName) condition handles the initial/empty values state while the second condition handles the error values.
{currentPage === 1 && (
<>
<BasicDetails />
<button
disabled={
!(
value.basicDetails.firstName && value.basicDetails.lastName
) ||
Object.values(value.basicDetails.errors).filter(Boolean).length
}
onClick={next}
>
Next
</button>
</>
)}
This pattern can be repeated for the following steps.
First you must be getting converting controlled component to uncontrolled component error in your console. For controlled component it is always preferred to use state to set value for the input. And with onChange handler you set the state. I will try to put into a single component so you would get the idea and apply your case
import React, {useState} from 'react';
import {isLettersOnly} from './components/utils'; // not sure where it is in your folder structure
const MyInputComponent = ({value, ...props}) => {
const [inputValue, setInputValue] = useState(value || ''); // input value should be empty string or undefined. null will not be accepted.
const [error, setError] = useState(null);
const handleChange = event => {
const { name, value } = event.target;
if (!isLettersOnly(value)) {
setError('Invalid Input');
}
setInputValue(value);
}
return (
<>
<input
value={inputValue}
onChange={handleChange}
{...props}
/>
{error && (
<span className={"error"}>{error}</span>
)}
</>
)
}
export default MyInputComponent;
This is a very rudimentary component. just to show the concept. You can then import this component as your input field and pass necessary props like name, className etc from parent.
import React from 'react';
import MyInputComponent from 'components/MyInputComponent';
const MyForm = (props) => {
return props.data && props.data.map(data=> (
<MyInputComponent
name="lastName"
className="form-control"
value={data.lastName}
));
}
When I try to type in the boxes on the webpage it doesn't register that I am typing anything. I am guessing it has something to do with the handleChange or onChange, but I could use some help here. I am still pretty new to React and trying to figure it out. What am I missing here?
import React, {Component} from 'react';
import { Form } from 'semantic-ui-react';
class Assessments extends Component {
state = {assessment_name: '', assessment_type: ''}
componentDidMount() {
if (this.props.id) {
const { assessment_name, assessment_type } = this.props
this.setState({ assessment_name, assessment_type })
}
}
handleChange = (a) => {
const { name, value } = a.target
this.setState({ [name]: value })
}
handleSubmit = (a) => {
a.preventDefault()
if (this.props.id) {
const { id, history } = this.props
this.props.updateName(id, this.state, history)
this.props.toggleUpdate()
}
this.props.close()
this.setState({ assessment_name: '', assessment_type: ''})
}
close = () => this.setState({ open: false })
render() {
const { assessment_name, assessment_type } = this.state
return(
<Form onSubmit={this.handleSubmit}>
<Form.Input
name=''
value={assessment_name}
onChange={this.handleChange}
label='Assessment Name'
required
/>
<Form.Input
name='AssessmentType'
value={assessment_type}
onChange={this.handleChange}
label='Assessment Type'
required
/>
<Form.Button>Submit</Form.Button>
</Form>
)
}
}
export default Assessments;
You're not passing the right names to the Form.Input components which the handleChange function uses to update the state. They have to be 'assessment_name' and 'assessment_type' respectively to make sure the state gets updated on input change events and the new values get reflected on the fields.
<>
<Form.Input
name="assessment_name"
value={assessment_name}
onChange={this.handleChange}
label="Assessment Name"
required
/>
<Form.Input
name="assessment_type"
value={assessment_type}
onChange={this.handleChange}
label="Assessment Type"
required
/>
</>
I have the following simple code and I am trying to rewrite it as a function avoiding classes and using hooks for learning purposes.
As you can see below, 'App' is extending 'Form'. The complete code includes other functions in 'Form', for example, a validation function which is called by 'handleChange' and modifies the 'errors' item in the state.
Note that 'Form' is not part of 'App' because Form will be reused by other components (such as a login component).
My main questions:
1- As per the documentation, they seem to discourage the use of Inheritance, how can I rewrite this without using "extends" (and keeping the classes)?
2- How can I rewrite this without classes?
So far, the only idea that came to my mind is to rewrite all the functions in form.jsx as independent functions and call them from App (see below). But this implies to write a lot of props and parameters (specially when validation is added as 'errors', 'setErrors', 'schema', etc. would be sent from 'App' to 'renderInput', from here to 'handleChange', etc.
It works but the code is less clean than before...
app.js
class App extends Form {
state = {
data: { username: "", password: "" },
};
render() {
return (
<form action="">
{this.renderInput("username", "Username")}
{this.renderInput("password", "Password", "password")}
</form>
);
}
}
form.jsx
class Form extends Component {
state = {
data: {},
};
handleChange = ({ currentTarget }) => {
const data = { ...this.state.data };
data[currentTarget.name] = currentTarget.value;
this.setState({ data });
};
renderInput(name, label, type = "text") {
const { data, errors } = this.state;
return (
<Input
name={name}
type={type}
value={data[name]}
label={label}
onChange={this.handleChange}
/>
);
}
render() {
return null;
}
}
export default Form;
input.jsx
const Input = ({ name, label, ...rest }) => {
return (
<div className="form-group">
<label htmlFor={name}>{label}</label>
<input {...rest} name={name} id={name} className="form-control" />
</div>
);
};
Attempt to change it into functions:
App.jsx
const App = () => {
const [user, setUser] = useState({ username: "", password: "" });
return (
<form action="">
{renderInput("username", "Username", user, setUser)}
{renderInput("password", "Password", user, setUser, "password")}
</form>
);
};
form.jsx
export function handleChange({ currentTarget }, data, setData) {
setData({ ...data, [currentTarget.name]: currentTarget.value });
}
export function renderInput(name, label, data, setData, type = "text") {
return (
<Input
name={name}
type={type}
value={data[name]}
label={label}
onChange={e => handleChange(e, data, setData)}
/>
);
}
Thanks in advance and let me know if you need a better explanation or the full code.
Move the form to the Form component and pass an array of inputs' properties to generate inputs:
App.jsx
const App = () => {
const [user, setUser] = useState({ username: "", password: "" });
const inputList = [
{name: "username", label: "Username", value: user.username},
{name: "password", label: "Password", value: user.password, type: "password"}
]
return (
<Form inputList={inputList} setData={setUser} />
);
};
Form.jsx
const Form = ({ inputList, setData }) => {
const handleChange = ({ currentTarget }) => {
const { name, value } = currentTarget;
setData(prevData => ({ ...prevData, [name]: value }));
};
return (
<form action="">
{
inputList.map(({ name, label, value, type = "text" }) =>
<Input
key={name}
name={name}
type={type}
value={value}
label={label}
onChange={handleChange}
/>
)
}
</form>
);
}
I have multiples components who needs input value
so I copy/paste my following code on the render method :
<input type="password"
name="password" value={user.password}
placeholder="password"
onChange={e => this.handleChange(e)}/>
And I copy/paste the handleChange method in all my component :
handleChange(event) {
const {name, value} = event.target
this.setState({
[name]: value,
})
}
I would like to have input in a component and call it from all my components and then get input value to add it to my current state component
Do you have any solution ?
Thanks
If I understand correctly you wan't something like this:
class MyWrapperComponent extends React.Component {
constructor(props) {
this.state = { password: '' }
}
handleChange(event) {
const {name, value} = event.target
this.setState({
[name]: value,
})
}
render() {
return (
<div>
<MyInputComponent value={this.state.password} onChange={this.handeChange.bind(this)} />
<MyDisplayPasswordComponent password={this.state.password} />
</div>
)
}
}
const MyInputComponent = (props) => {
return (
<input type="password"
name="password" value={props.password}
placeholder="password"
onChange={props.onChange}/>
)
}
const MyDisplayPasswordComponent = (props) => {
return <h1>Your secret password is {props.password}</h1>
}