Using Dynamic Var with `Set` State in React Hooks? - javascript

This is a pretty common pattern in React components:
handleTextFieldChange(event)
{
const name = event.currentTarget.name;
this.setState({[name]: event.currentTarget.value})
}
What Javascript syntax could be used to do the same with React hooks?
i.e. something possibly along the lines of:
handleTextFieldChange(event)
{
const name = event.currentTarget.name;
this.set[name](event.currentTarget.value);
}

You could use a single useState with a default value of an object that contains all your input values and update that like you are used to with class components.
Example
const { useState } = React;
function App() {
const [state, setState] = useState({ email: "", password: "" });
function onChange(event) {
const { name, value } = event.target;
setState(prevState => ({ ...prevState, [name]: value }));
}
return (
<div>
<input value={state.email} name="email" onChange={onChange} />
<input value={state.password} name="password" onChange={onChange} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

How about something like this?
function handleTextFieldChange(mySetFunction, event) {
const value = event.currentTarget.value;
mySetFunction(value);
}
<TextField
placeholder="Email"
name="passwordResetEmailAddress"
onChange={(e) => handleTextFieldChange(setPasswordResetEmailAddress, e)}
>
{passwordResetEmailAddress}
</TextField>
I've tested it and it works.

class Yup extends React.Component {
state = {
first: "",
second: ""
};
handleTextFieldChange = ({ target: { name, value } }) =>
this.setState({ [name]: value });
render() {
const { first, second } = this.state;
return (
<div>
<p>{first}</p>
<p>{second}</p>
<input type="text" name="first" onChange={this.handleTextFieldChange} />
<input
type="text"
name="second"
onChange={this.handleTextFieldChange}
/>
</div>
);
}
}
same with hook
function Yup() {
const [{ first, second }, setState] = useState({ first: "", second: "" });
function handleTextFieldChange({ target: { name, value } }) {
setState(prevState => ({ ...prevState, [name]: value }));
}
return (
<div>
<p>{first}</p>
<p>{second}</p>
<input type="text" name="first" onChange={handleTextFieldChange} />
<input type="text" name="second" onChange={handleTextFieldChange} />
</div>
);
}

You can dynamically update a state for the target field by receiving an update state function as an argument in onChange function.
Example
import React, { useState } from "react";
const App = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const onChangeHandler = (setIdentifierState, event) => {
setIdentifierState(event.target.value);
};
return (
<div>
<p>{"Email: " + email}</p>
<p>{"Password: " + password}</p>
<input
type="text"
placeholder="email"
onChange={onChangeHandler.bind(null, setEmail)}
/>
<input
type="text"
placeholder="password"
onChange={onChangeHandler.bind(null, setPassword)}
/>
</div>
);
};
export default App;

I recently tackled this same problem while converting my components from classes to functions. I ended up creating an object that could then point to the separate state hooks:
const textStates = {};
const setTextStates= {};
for (var name of names) {
let [textState, setTextState] = useState("");
textStates[name] = textState;
setTextStates[name] = setTextState;
}

I solved it this way (a slightly more dynamic solution than what #VikR offered)
const [title, setTitle] = useState("");
const [desc, setDesc] = useState("");
const [video, setVideo] = useState("");
const handleChange = setter => event => {
const value = event.target.value;
//special cases
if (setter === setVideo) {
setInvalidVideo(!ReactPlayer.canPlay(value))
}
setter(value)
}
Then in my code:
<TextField fullWidth value={title} type="date"
label={t('service.ticket.add.title')}
placeholder={t('service.ticket.add.titlePh')}
onChange={handleChange(setTitle)} variant="outlined"/>

Related

How to transfer data between React pages?

I want to pass the states into a object, because after I'll post an API. How can I store the states into the object? If I call the function setAll() causes a infinite loop.
Like an example the console.log returns infinite times in the console.
import React,{useState} from 'react';
import Signup1 from '../pages/Signup/steps/Signup1';
import Signup2 from '../pages/Signup/steps/Signup2';
import Signup3 from '../pages/Signup/steps/Signup3';
function Forms() {
const [alert,setAlert]=useState();
const [page,setPage] = useState(0);
const [formData,setFormData]= useState({
email :"",
FirstName:"",
LastName:"",
CountryNumber:"",
Number:"",
JobTitle:"",
People:"",
Company:"",
TaxID:""
})
const [email,setEmail] = useState("");
const [firstName,setFirstName] = useState("");
const [lastName,setLastName] = useState("");
const [countryNumber,setCountryNumber] = useState("");
const [number,setNumber] = useState("");
const [jobTitle,setJobTitle] = useState("");
const [people,setPeople] = useState("");
const [company,setCompany] = useState("");
const [taxID,setTaxID] = useState("");
const setAll = ()=>{
setFormData({
email :email,
FirstName:firstName,
LastName:lastName,
CountryNumber:countryNumber,
Number:number,
JobTitle:jobTitle,
People:people,
Company:company,
TaxID:taxID
})
console.log(formData)
}
const PageDisplay = () =>{
if(page===0){
return <Signup1
email={email}
setEmail={setEmail}
setPage={setPage}
/>;
}
if(page===1){
return <Signup2
firstName={firstName}
setFirstName={setFirstName}
lastName={lastName}
setLastName={setLastName}
countryNumber={countryNumber}
setCountryNumber={setCountryNumber}
number={number}
setNumber={setNumber}
jobTitle={jobTitle}
setJobTitle={setJobTitle}
setPage={setPage}/>;
}
if(page===2){
return <Signup3
people={people}
setPeople={setPeople}
company={company}
setCompany={setCompany}
taxID={taxID}
setTaxID={setTaxID}
page={page}
setPage={setPage}
/>
}
if(page===3){
return( <div>sucess</div>)
}
}
/*
if(page===3){
setAll()
return<div>sucess</div>
}
*/
return (
<div className='Forms'>
{PageDisplay()}
</div>
);
}
export default Forms;
Don't call setAll() like this, functions that set state like this should be called on the occurrence of an event (like an onClick) or in a useEffect, whenever you set a state in the component body, the component re-renders which triggers another state set, thus causing an infinite loop, in your case, it's better to set the form body when you call the API. Setting states is a costly operation, so it's better to set form body state finally when you are going to hit the API.
Making a working example always helps someone to create a better and more precise answer to your question. This is how you set your data.
Also if you want to send this data to another component/function you can do it by passing formData object, you don't need to set it one by one.
Please try to ask any questions you may have.
To see the document clearly, expand the snippet to fill the entire page.
const {useEffect, useState} = React;
const Forms = () => {
const [alert,setAlert]=useState();
const [page,setPage] = useState(0);
const [formData,setFormData]= useState({
email :"",
FirstName:"",
LastName:"",
CountryNumber:"",
Number:"",
JobTitle:"",
People:"",
Company:"",
TaxID:""
})
const [email,setEmail] = useState("");
const [firstName,setFirstName] = useState("");
const [lastName,setLastName] = useState("");
const [countryNumber,setCountryNumber] = useState("");
const [number,setNumber] = useState("");
const [jobTitle,setJobTitle] = useState("");
const [people,setPeople] = useState("");
const [company,setCompany] = useState("");
const [taxID,setTaxID] = useState("");
useEffect(() => {
console.log(formData);
}, [formData]);
useEffect(() => {
setAll();
}, [email, firstName, lastName, countryNumber, number, jobTitle, people, company, taxID]);
const setAll = () => {
setFormData({
email :email,
FirstName:firstName,
LastName:lastName,
CountryNumber:countryNumber,
Number:number,
JobTitle:jobTitle,
People:people,
Company:company,
TaxID:taxID
})
}
const Signup2 = ({data}) => {
return <div>Email: {data.email}</div>;
}
const Signup3 = ({data}) => {
return "Signup3 Page Content";
}
const PageDisplay = () =>{
if(page===0){
return <form>
<input onChange={(e) => setEmail(e.target.value)} value={email} type="text" />
<input onChange={(e) => setFirstName(e.target.value)} value={firstName} type="text" />
<input onChange={(e) => setLastName(e.target.value)} value={lastName} type="text" />
<input onChange={(e) => setCountryNumber(e.target.value)} value={countryNumber} type="text" />
<input onChange={(e) => setNumber(e.target.value)} value={number} type="text" />
<input onChange={(e) => setJobTitle(e.target.value)} value={jobTitle} type="text" />
<input onChange={(e) => setPeople(e.target.value)} value={people} type="text" />
<input onChange={(e) => setCompany(e.target.value)} value={company} type="text" />
<input onChange={(e) => setTaxID(e.target.value)} value={taxID} type="text" />
</form>;
}
if(page===1){
return <Signup2 data={formData} />;
}
if(page===2){
return <Signup3 data={formData} />
}
if(page===3){
return( <div>sucess</div>)
}
}
return (
<div>
<button onClick={() => setAll()}>Trigger SetAll</button>
<div>Current Page {page + 1}</div>
<button onClick={() => setPage(0)}>Page 1</button>
<button onClick={() => setPage(1)}>Page 2</button>
<button onClick={() => setPage(2)}>Page 3</button>
<div>{PageDisplay()}</div>
</div>
);
};
ReactDOM.render(
<Forms />,
document.getElementById("root")
);
input[type="text"]{
display:block;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

How to pass multiple onChange form data from child to parent element in react

I am trying to print real-time user input from input tags by the user. I am even getting multiple user inputs from the child element to the parent element as a form of an object using useState. But whenever the user tries to fill the second input field, then the first input is re-render and it's replaced by the primary state which is an empty string.
code:-
Child Element
import React, { useState } from "react";
const Child = (props) => {
const [name, setName] = useState("");
const [age, setAge] = useState("");
let userData = {
name: "",
age: ""
};
const nameChangeHandler = (e) => {
setName(e.target.value);
userData.name = e.target.value;
};
const ageChangeHandler = (e) => {
setAge(e.target.value);
userData.age = e.target.value;
};
const formOnChageHandler = (e) => {
e.preventDefault();
props.getData(userData);
};
const fromOnSubmitHandler = (e) => {
e.preventDefault();
};
return (
<React.Fragment>
<form onChange={formOnChageHandler} onSubmit={fromOnSubmitHandler}>
<label htmlFor="name">Name:</label>
<input
id="name"
placeholder="Enter Name"
value={name}
onChange={nameChangeHandler}
/>
<br />
<label htmlFor="age">Age:</label>
<input
id="age"
placeholder="Enter Age"
value={age}
onChange={ageChangeHandler}
/>
</form>
</React.Fragment>
);
};
export default Child;
Parent Element
import React, { useState } from "react";
import Child from "./components/Child";
function App() {
const [name, setName] = useState("");
const [age, setAge] = useState("");
let userData = (data) => {
setName(data.name);
setAge(data.age);
};
return (
<React.Fragment>
<Child getData={userData} />
<h1>Your name is:{name}</h1>
<h1>Your age is:{age}</h1>
</React.Fragment>
);
}
export default App;
code sandbox Link- https://codesandbox.io/s/from-traversing-child-to-parent-to-another-child-ynwyqd?file=/src/App.js:0-441
How I can get both data being reflected by using onChange from child to parent element?
I suggest you accumulate the user data in one state.
Like this.
const [user, setUser] = useState({
name: "",
age: null
});
And put the state on the parent and pass as props, also just have one handleChange function to update both the name and age by the input id
Child.js
import React, { useState } from "react";
const Child = ({ user, setUser }) => {
const handleChange = (e) => {
setUser((prev) => ({
...prev,
[e.target.id]: e.target.value
}));
};
const formOnChageHandler = (e) => {
e.preventDefault();
};
const fromOnSubmitHandler = (e) => {
e.preventDefault();
};
return (
<React.Fragment>
<form onChange={formOnChageHandler} onSubmit={fromOnSubmitHandler}>
<label htmlFor="name">Name:</label>
<input
id="name"
placeholder="Enter Name"
value={user.name}
onChange={handleChange}
/>
<br />
<label htmlFor="age">Age:</label>
<input
id="age"
placeholder="Enter Age"
value={user.age}
onChange={handleChange}
/>
</form>
</React.Fragment>
);
};
export default Child;
App.js
import React, { useState } from "react";
import Child from "./components/Child";
function App() {
const [user, setUser] = useState({
name: "",
age: null
});
return (
<React.Fragment>
<Child user={user} setUser={setUser} />
<h1>Your name is:{user.name}</h1>
<h1>Your age is:{user.age}</h1>
</React.Fragment>
);
}
export default App;
CODESANDBOX
Try using the child component as below,
import React, { useState } from "react";
const Child = (props) => {
const [name, setName] = useState("");
const [age, setAge] = useState("");
let userData = {
name: name, // the value "name" comes for the local state will be listen to the onChange event every time
age: age // same applies here as well
};
const nameChangeHandler = (e) => {
setName(e.target.value);
};
const ageChangeHandler = (e) => {
setAge(e.target.value);
};
const formOnChageHandler = (e) => {
e.preventDefault();
props.getData(userData);
};
const fromOnSubmitHandler = (e) => {
e.preventDefault();
};
return (
<React.Fragment>
<form onSubmit={fromOnSubmitHandler}>
<label htmlFor="name">Name:</label>
<input
id="name"
placeholder="Enter Name"
value={name}
onChange={nameChangeHandler}
/>
<br />
<label htmlFor="age">Age:</label>
<input
id="age"
placeholder="Enter Age"
value={age}
onChange={ageChangeHandler}
/>
</form>
</React.Fragment>
);
};
export default Child;
just use simple one state to manage data. just take a look below example component created from your child component.
we simply use single object state.
use name prop as key to store value in state.
import React, { useState } from "react";
const Child = (props) => {
const [formData, setFormData] = useState({});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
const formOnChageHandler = (e) => {
e.preventDefault();
props.getData(userData);
};
const fromOnSubmitHandler = (e) => {
e.preventDefault();
};
return (
<React.Fragment>
<form onChange={formOnChageHandler} onSubmit={fromOnSubmitHandler}>
<label htmlFor="name">Name:</label>
<input
id="name"
placeholder="Enter Name"
value={name}
onChange={handleChange}
name="name"
/>
<br />
<label htmlFor="age">Age:</label>
<input
id="age"
placeholder="Enter Age"
value={age}
onChange={handleChange}
name="age"
/>
</form>
</React.Fragment>
);
};
export default Child;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
it happens because you dont watch to the state, try this:
Child.js
import React, { useState } from "react";
const Child = (props) => {
const [name, setName] = useState("");
const [age, setAge] = useState("");
let userData = {
name,
age
};
const nameChangeHandler = (e) => {
setName(e.target.value);
userData.name = e.target.value;
};
const ageChangeHandler = (e) => {
setAge(e.target.value);
userData.age = e.target.value;
};
const formOnChageHandler = (e) => {
e.preventDefault();
props.getData(userData);
};
const fromOnSubmitHandler = (e) => {
e.preventDefault();
};
return (
<React.Fragment>
<form onChange={formOnChageHandler} onSubmit={fromOnSubmitHandler}>
<label htmlFor="name">Name:</label>
<input
id="name"
placeholder="Enter Name"
value={name}
onChange={nameChangeHandler}
/>
<br />
<label htmlFor="age">Age:</label>
<input
id="age"
placeholder="Enter Age"
value={age}
onChange={ageChangeHandler}
/>
</form>
</React.Fragment>
);
};
export default Child;
Try this, i check in codesandbox and it works:
In App.js:
import React, { useState } from "react";
import Child from "./components/Child";
function App() {
const [name, setName] = useState("");
const [age, setAge] = useState("");
return (
<React.Fragment>
<Child name={name} age={age} setName={setName} setAge={setAge} />
<h1>Your name is:{name}</h1>
<h1>Your age is:{age}</h1>
</React.Fragment>
);
}
export default App;
In Child.js:
import React, { useState } from "react";
const Child = ({ name, age, setName, setAge }) => {
const nameChangeHandler = (e) => {
setName(e.target.value);
};
const ageChangeHandler = (e) => {
setAge(e.target.value);
};
const fromOnSubmitHandler = (e) => {
e.preventDefault();
};
return (
<React.Fragment>
<form onSubmit={fromOnSubmitHandler}>
<label htmlFor="name">Name:</label>
<input
id="name"
placeholder="Enter Name"
value={name}
onChange={nameChangeHandler}
/>
<br />
<label htmlFor="age">Age:</label>
<input
id="age"
placeholder="Enter Age"
value={age}
onChange={ageChangeHandler}
/>
</form>
</React.Fragment>
);
};
export default Child;
If you want to improve your code, you can research and use state management like: redux, zustand, react context,...
Hope it useful for you.

am getting an error props.onSubmitForm is not a function while trying to pass data from child to parent component in react

I am trying to store input data in two states; name and age when onChange event is fired.
Thereafter I am trying to pass the stored data upon one level when an onSubmit event of a form is fired.
What I have realized is that when the form is submitted and it is now the time to pass the data up one level, I get an error "props.onSubmitForm is not a function".
I have noted this is a common error but i have tried comparing answers and code to no avail.
here is the child component with the form.
const UserDetails = (props) => {
const [name, setName] = useState("");
const [age, setAge] = useState("");
const submittedFormhandler = (event) => {
event.preventDefault();
let userDetails = {
name: name,
age: age
};
props.onSubmitForm(userDetails);
};
const getNameHandler = (event) => {
return setName(event.target.value);
};
const getAgeHandler = (event) => {
return setAge(event.target.value);
};
return (
<form onSubmit={submittedFormhandler}>
<div>
<label htmlFor="name">User name</label>
<input type="text" name="name" id="" onChange={getNameHandler} />
</div>
<div>
<label htmlFor="age">Age</label>
<input type="number" name="age" id="" onChange={getAgeHandler} />
</div>
<button type="submit">Add User</button>
</form>
);
};
export default UserDetails;
here is the parent component
import UserDetails from "../UserDetails";
const DisplayDetails = () => {
const submittedDataHandler = (inputData) => {
return console.log(inputData);
};
return <UserDetails onSubmitForm={submittedDataHandler} />;
};
export default DisplayDetails;
Your code works super fine, but I guess that's because you've used the wrong component
It should be DisplayDetails not UserDetails
This one works fine:
const DisplayDetails = () => {
const submittedDataHandler = (inputData) => {
return console.log(inputData);
};
return <UserDetails onSubmitForm={submittedDataHandler} />;
};
const UserDetails = (props) => {
const [name, setName] = React.useState("");
const [age, setAge] = React.useState("");
const submittedFormhandler = (event) => {
event.preventDefault();
let userDetails = {
name: name,
age: age
};
props.onSubmitForm(userDetails);
};
const getNameHandler = (event) => {
return setName(event.target.value);
};
const getAgeHandler = (event) => {
return setAge(event.target.value);
};
return (
<form onSubmit={submittedFormhandler}>
<div>
<label htmlFor="name">User name</label>
<input type="text" name="name" id="" onChange={getNameHandler} />
</div>
<div>
<label htmlFor="age">Age</label>
<input type="number" name="age" id="" onChange={getAgeHandler} />
</div>
<button type="submit">Add User</button>
</form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<DisplayDetails />, rootElement); // <-- RIGHT COMPONENT!!!
<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="root"></div>
And this one throw error like yours:
const DisplayDetails = () => {
const submittedDataHandler = (inputData) => {
return console.log(inputData);
};
return <UserDetails onSubmitForm={submittedDataHandler} />;
};
const UserDetails = (props) => {
const [name, setName] = React.useState("");
const [age, setAge] = React.useState("");
const submittedFormhandler = (event) => {
event.preventDefault();
let userDetails = {
name: name,
age: age
};
props.onSubmitForm(userDetails);
};
const getNameHandler = (event) => {
return setName(event.target.value);
};
const getAgeHandler = (event) => {
return setAge(event.target.value);
};
return (
<form onSubmit={submittedFormhandler}>
<div>
<label htmlFor="name">User name</label>
<input type="text" name="name" id="" onChange={getNameHandler} />
</div>
<div>
<label htmlFor="age">Age</label>
<input type="number" name="age" id="" onChange={getAgeHandler} />
</div>
<button type="submit">Add User</button>
</form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<UserDetails />, rootElement); // <-- WRONG COMPONENT HERE!!!
<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="root"></div>
The wrong version of your code which should be DisplayDetails:

React Js : How to use UseState In CallBack?

I have below code :
import React,{useState} from 'react'
function ReactForm() {
const iState =[{
Name : '',
Email :'',
Salary :0
}]
const [state, setstate] = useState(iState);
function validationHandler()
{
console.log(state);
}
return (
<div>
Name : <input type="text" onChange={(e)=>{setstate(...state, state.Name=e.target.value)}}></input>
<br></br>
Email : <input type="text" onChange={(e)=>{setstate(...state, state.Email=e.target.value)}}></input>
<br></br>
Salary : <input type="text" onChange={(e)=>{setstate(...state, state.Salary=e.target.value)}}></input>
<br></br>
<button onClick={validationHandler}>Validate Us</button>
</div>
)
}
export default ReactForm
I am performing basic validations here. I am receiving error : TypeError: state is not iterable
After going through few links on stackoverflow , I added - [ ] over state , but it did not helped.
EDIT 1 :
After Adding :- setstate({...state, state.Name: e.target.value}) : Unexpected token, expected "," (18:79)
Instead of having the setState called for each of the inputs you can make use of the name attribute and can refactor the code as below
import React, {useState} from 'react';
function ReactForm() {
const [state, setstate] = useState({
Name: '',
Email: '',
Salary: 0,
});
const handleChange = (e) => {
const {name, value} = e.target;
setstate((prevState) => ({...prevState, [name]: value}));
};
function validationHandler() {
console.log(state);
}
return (
<div>
Name :{' '}
<input
type="text"
value={state.Name}
name="Name"
onChange={handleChange}
/>
<br></br>
Email :{' '}
<input
type="text"
value={state.Email}
name="Email"
onChange={handleChange}
/>
<br></br>
Salary :{' '}
<input
type="text"
value={state.Salary}
name="Salary"
onChange={handleChange}
/>
<br></br>
<button onClick={validationHandler}>Validate Us</button>
</div>
);
}
export default ReactForm;
Refer:
Controlled Component
Your initial state is an array of objects. I'm not sure whether this is what you are looking for.
Assume your iState is (Just an object)
const iState = {
Name: '',
Email: '',
Salary: 0
}
Then you should do something like this in your onChange listener
// setState should use camel case for best pratice BTW
const handleChangeName = e => setstate({
...state,
Name: e.target.value
});
If you are sticking to the array state, the listener should look something like this instead.
const handleChangeName = e => setstate([
...state,
{
...state[0], // or whatever index you may use in the future
Name: e.target.value
}
]);
You can do the following assignment state.Name=e.target.value ****:
You are using an array not an object, so there is nothing you can access using state.Name=e.target.value
So if wanna access it directly the same way you used you have to use state property as OBJECT not as ARRAY:
const iState = {
Name: '',
Email: '',
Salary: 0
}
And the standard for the component that has form to handle is to use stateful component
OR
You can use stateless (functional) component and make form each form field its own state:
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [salary, setSalary] = useState(0);
So the component will be:
import React, { useState } from 'react'
function ReactForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [salary, setSalary] = useState(0)
function validationHandler() {
console.log('Name: ' + name);
console.log('Email: ' + email);
console.log('Salary: ' + salary);
}
return (
<div>
Name : <input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}></input>
<br></br>
Email : <input
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}></input>
<br></br>
Salary : <input
type="text"
value={salary}
onChange={(e) => setSalary(e.target.value)}></input>
<br></br>
<button onClick={validationHandler}>Validate Us</button>
</div>
)
}
export default ReactForm;

React Hooks: handle multiple inputs

on react docs forms section there is the following example using class components:
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
Considering Hooks can only be called either in a React function component or a custom React Hook function is there a way of doing it using hooks instead?
you can clean up #adam 's final solution a bit by not using the useCallback hook, and instead simply using the useState hook as a controlled component.
const MyComponent = () => {
const [inputs, setInputs] = useState({});
const handleChange = e => setInputs(prevState => ({ ...prevState, [e.target.name]: e.target.value }));
return (
<>
<input name="field1" value={inputs.field1 || ''} onChange={handleChange} />
<input name="field2" value={inputs.field2 || ''} onChange={handleChange} />
</>
)
}
example
const MyComponent = () => {
const [inputs,setInputs] = useState({});
return (
<>
<input key="field1" name="field1" onChange={({target}) => setInputs(state => ({...state,field1:target.value}))} value={inputs.field1}/>
<input key="field2" name="field2" onChange={({target}) => setInputs(state => ({...state,field2:target.value}))} value={inputs.field2}/>
</>
)
}
you can pass in initial values like this:
const MyComponent = (initialValues = {}) => {
const [inputs,setInputs] = useState(initialValues);
...
}
EDIT: A nice short onChange according to #hamidreza's comment
const MyComponent = (initialValues = {}) => {
const [inputs,setInputs] = useState(initialValues);
const onChangeHandler = useCallback(
({target:{name,value}}) => setInputs(state => ({ ...state, [name]:value }), [])
);
return (
<>
<input key="field1" name="field1" onChange={onChangeHandler} value={inputs.field1}/>
<input key="field2" name="field2" onChange={onChangeHandler} value={inputs.field2}/>
</>
)
}
etc, etc, etc
Maybe, on the last example onChangeForField('...') will be triggered on each render, so maybe you have to write onChange={()=>onChangeForField('...')} or if you want the event to get passed onChange={(e)=>onChangeForField('...', e)}
I was looking for the same answer,but i was finding difficulty to understand the previous solutions,so i tried in my own way ,and i found a solution.
const [inputs,setInputs] = useState({
'field1':'',
'field2':'',
});
const handleChange = (e) => {
const name = e.target.name; //it is the name of that input
const value = e.target.value; //value of that input
setInputs((prev) => {
prev[name] = value;//changing the updated value to the previous state
return prev;
});
};
return (
<>
<input key="field1" name="field1" onChange={handleChange} value={inputs.field1}/>
<input key="field2" name="field2" onChange={handleChange} value={inputs.field2}/>
</>
adding to Adam's answer and for those who are looking towards typescript solution,
interface MyIType {
field1: string;
...
}
//Partial from typescript to make properties optional
interface MyFormType extends Partial<MyIType> {}
const [inputs,setInputs] = useState<MyFormType>(initialValues);
const onChangeForField = useCallback(({target}) =>
setInputs(_state => {
return {
..._state,
[target.name]: target.value,
};
}),
[]
);
If you were like me, having multiple inputs on multiple pages using the same input id/name/key, try value={data.xxx || ''} .
Full code:
const [data, setData] = useState<any>({});
const handleValueChanges = e => {
setData({
...data,
[e.target.name]: e.target.value,
});
};
<InputText (using prime react)
id="firstName"
name="firstName"
value={data.firstName || ''}
onChange={handleUpdate}
/>
As of v6 you can use .forEach(), Please refer to the migrate guide
[{name: "firstName", value: "Safwat" }, {name: "lastName", value: "Fathi", }].forEach(({name, value}) => setValue(name, value));

Categories

Resources