I found that there are several ways to handle user's text input with hooks. What is more preferable or proper way to handle an input with hooks? Which would you use?
1) The simplest hook to handle input, but more fields you have, more repetitive code you have to write.
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
events:
onChange={event => setPassword(event.target.value)}
onChange={event => setUsername(event.target.value)}
2) Similar to above example, but with dynamic key name
const [inputValues, setInputValues] = useState({
username: '', password: ''
});
const handleOnChange = event => {
const { name, value } = event.target;
setInputValues({ ...inputValues, [name]: value });
};
event:
onChange={handleOnChange}
3) An alternative to useState, and as said on ReactJS docs, useReducer is usually preferable to useState.
const [inputValues, setInputValues] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{username: '', password: ''}
);
const handleOnChange = event => {
const { name, value } = event.target;
setInputValues({ [name]: value });
};
event:
onChange={handleOnChange}
4) useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.
const [inputValues, setInputValues] = useState({
username: '', password: ''
});
const handleOnChange = useCallback(event => {
const { name, value } = event.target;
setInputValues({ ...inputValues, [name]: value });
});
event:
onChange={handleOnChange}
How about writing a reusable function that returns the input value ... and the <input> itself:
function useInput({ type /*...*/ }) {
const [value, setValue] = useState("");
const input = <input value={value} onChange={e => setValue(e.target.value)} type={type} />;
return [value, input];
}
That can then be used as:
const [username, userInput] = useInput({ type: "text" });
const [password, passwordInput] = useInput({ type: "text" });
return <>
{userInput} -> {username} <br />
{passwordInput} -> {password}
</>;
This is how i'm using right now:
const [inputValue, setInputValue] = React.useState("");
const onChangeHandler = event => {
setInputValue(event.target.value);
};
<input
type="text"
name="name"
onChange={onChangeHandler}
value={inputValue}
/>
Yes you can handle react hooks with useState()
import React, {useState} from 'react'
export default () => {
const [fName, setfName] = useState('');
const [lName, setlName] = useState('');
const [phone, setPhone] = useState('');
const [email, setEmail] = useState('');
const submitValue = () => {
const frmdetails = {
'First Name' : fName,
'Last Name' : lName,
'Phone' : phone,
'Email' : email
}
console.log(frmdetails);
}
return(
<>
<hr/>
<input type="text" placeholder="First Name" onChange={e => setfName(e.target.value)} />
<input type="text" placeholder="Last Name" onChange={e => setlName(e.target.value)} />
<input type="text" placeholder="Phone" onChange={e => setPhone(e.target.value)} />
<input type="text" placeholder="Email" onChange={e => setEmail(e.target.value)} />
<button onClick={submitValue}>Submit</button>
</>
)
}
Here's how I do it (assuming your inputs must be inside a form):
I have a BasicForm component that I use.
It stores all the inputs state into an object into a single useState() call.
It passes via useContext() the inputs state along with an onChange() function and a function setInputInitialState() for the inputs to set their initial state when they are first mounted. It also passes onFocus, onBlur, and it has functions to validate fields which I'm not showing here to simplify the code.
This way I can easily create a form with as many inputs as I want, like:
<BasicForm
isSubmitting={props.isSubmitting}
submitAction={ (formState) =>
props.doSignIn(formState) }
>
<TextInput
type='email'
label='Email'
name='email'
placeholder='Enter email...'
required
/>
<TextInput
type='password'
label='Password'
name='password'
placeholder='Enter password...'
min={6}
max={12}
required
/>
<SubmitButton
label='Login'
/>
</BasicForm>
BasicForm.js
import FormContext from './Parts/FormContext';
function BasicForm(props) {
const [inputs, setInputs] = useState({});
function onChange(event) {
const newValue = event.target.value;
const inputName = event.target.name;
setInputs((prevState)=> {
return({
...prevState,
[inputName]: {
...prevState[inputName],
value: newValue,
dirty: true
}
});
});
}
function setInputInitialState(
inputName,
label='This field ',
type,
initialValue = '',
min = false,
max = false,
required = false) {
const INITIAL_INPUT_STATE = {
label: label,
type: type,
onFocus: false,
touched: false,
dirty: false,
valid: false,
invalid: false,
invalidMsg: null,
value: initialValue,
min: min,
max: max,
required: required
};
setInputs((prevState) => {
if (inputName in prevState) {
return prevState;
}
return({
...prevState,
[inputName]: INITIAL_INPUT_STATE
});
});
}
return(
<FormContext.Provider value={{
onChange: onChange,
inputs: inputs,
setInputInitialState: setInputInitialState,
}}>
<form onSubmit={onSubmit} method='POST' noValidate>
{props.children}
</form>
</FormContext.Provider>
);
}
TextInput.js
The inputse use the useEffect() hook to set their initial state when they're mounted.
function TextInput(props) {
const formContext = useContext(FormContext);
useEffect(() => {
console.log('TextInput useEffect...');
formContext.setInputInitialState(
props.name,
props.label,
props.type,
props.initialValue,
props.min,
props.max,
props.required
);
},[]);
return(
<input
type={props.type}
id={props.name}
name={props.name}
placeholder={props.placeholder}
value={([props.name] in formContext.inputs) ?
formContext.inputs[props.name].value
: props.initialValue || ''}
onChange={formContext.onChange}
onFocus={formContext.onFocus}
onBlur={formContext.onBlur}
>
</input>
</div>
{([props.name] in formContext.inputs) ?
formContext.inputs[props.name].invalidMsg && <div><span> {formContext.inputs[props.name].invalidMsg}</span></div>
: null}
</div>
);
...
}
function App(){
const [name, setName] = useState("");
const [istrue, Setistrue] = useState(false);
const [lastname,setLastname]=useState("");
function handleclick(){
Setistrue(true);
}
return(
<div>
{istrue ? <div> <h1>{name} {lastname}</h1> </div> :
<div>
<input type="text" placeholder="firstname" name="name" onChange={e =>setName(e.target.value)}/>
<input type="text" placeholder="lastname" name="lastname" onChange={e =>setLastname(e.target.value)}/>
<button type="submit" onClick={handleclick}>submit</button>
</div>}
</div>
)
}
}
You may want to consider a form library like Formik
Related
Unable to get the value of the checked input (true or false).
function Login() {
const [inputs, setInputs] = useState({});
const handleChange = (event) => {
const name = event.target.name;
const value = event.target.value;
setInputs(values => ({
...values,
[name]: value
}));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log({inputs});
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="mail" value={inputs.mail || ""} onChange={handleChange}/>
<input type="password" name="pass" value={inputs.pass || ""} onChange={handleChange}/>
<div>
<input type="checkbox" name="check" value={inputs.check || false} onChange={handleChange}/>
<label>Remember me</label>
</div>
<button type="submit">Submit</button>
</form>
);
}
export default LoginGoogle
Tried
const handleChange = (event) => {
const name = event.target.name;
const value = event.target.value;
const check = event.target.checked;
setInputs(values => ({
...values,
[name]: value || check
}));
};
For
<input type="checkbox" name="check" value={inputs.checked} onChange={handleChange}/>
And
<input type="checkbox" name="check" checked={inputs.checked} onChange={handleChange}/>
It works, but I am certain I am going about it wrong. Tutorials seem to concentrate on input objects of similar key-values, e.g. multiple checkboxes, multiple text input, and so on.
The reason is that checkbox values never change, only their checked property does.
Ideally, you'd use a different change handler
const onCheckboxChange = ({ target: { name, checked } }) => {
setInputs((prev) => ({
...prev,
[name]: checked,
}));
};
return (
<input
type="checkbox"
name="check"
checked={inputs.check}
onChange={onCheckboxChange}
/>
);
God Day, I'm trying to pass a Boolean value of the checkbox using onChange but onChange is already use to toggle the checkbox value. I dont have idea on what will be the other way around. Please guide me Thank you much.
function ExperienceForm() {
const [postData, setPostData] = useState({intern: ''});
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
dispatch(createPost(postData))
}
const [state, setState] = React.useState({
intern: false,
});
const handleChange = (event) => {
setState({ ...state, [event.target.name]: event.target.checked });
console.log(state.intern);
};
return (
<form autoComplete="off" noValidate className="form" onSubmit={handleSubmit}>
<FormControlLabel control={
<Checkbox
checked={state.intern}
onChange={handleChange ((e) => setPostData({ ...postData, intern: e.target.value }))}
name="intern"
color="primary"
value={state.intern}
/>
}
label="Intern"
/><br/>
<Button className="button" variant="container" color="primary" size="large" type="submit" block>Submit</Button>
</form>
);
}
export default ExperienceForm;
I don't see code of your <FormControlLabel /> and <Checkbox /> components, but with regular html input you can do it like this:
import React, { useState } from "react";
function ExperienceForm() {
const [postData, setPostData] = useState({ intern: false });
const [state, setState] = useState({ intern: false });
const handleChange = ({ target }) => {
setState({ ...state, [target.name]: target.checked });
setPostData({ ...postData, intern: target.checked });
};
return (
<form autoComplete="off" noValidate className="form">
<h2>postData.intern: {postData.intern.toString()}</h2>
<h2>state.intern: {state.intern.toString()}</h2>
<input
type="checkbox"
checked={state.intern}
onChange={handleChange}
name="intern"
color="primary"
value={state.intern}
/>
<button type="submit">Submit</button>
</form>
);
}
export default ExperienceForm;
in react.js i add a name attribute to each element For example:
handleChange1(event) {
const {name, value} = event.target
this.setState({[name]: value});
}
<input name="inputone" type="text" value={this.state.value1} onChange={this.handleChange1} />
But how do I do that in react-hook?
for example:
<Input
name="FirstName"
onChange={onChangeFirstName}
></Input>
const onChangeFirstName = (event) => {
setFirstName(event.target.value);
};
In fact, my question is, if I have multiple inputs, how can I write a handleChange for them?
I did ask similar question a month ago, about using hooks with multiple state, with one onChangeHandler using hooks:
const [name, setName] = useState({
firstName: '',
middleName: '',
lastName: '',
})
const onChangeHandler = (event) => {
const {name, value} = event.target;
setName({
...name,
[name]: value,
})
}
return(
<div>
<input
type="text"
name="fistName"
value={firstNmae}
onChange={onChangeHandler}
/>
</div>
)
How to use the updated state after setState call. In my below code, I am getting the previous state value.
For array below technique works.
setState(currentState => [...currentState, {name, value}]);
But for object, its not working.
import React, { useState } from "react";
export default function App() {
const [state, setState] = useState({});
const handleChange = e => {
const { name, value } = e.target;
setState(currentState => ({ ...currentState, [name]: value }));
console.log(state[name]);
};
const handleSubmit = e => {
e.preventDefault();
console.log(state);
};
return (
<div className="App">
<form onSubmit={handleSubmit}>
a
<input
type="text"
name="a"
value={state["a"] || ""}
onChange={handleChange}
/>
b
<input
type="text"
name="b"
value={state["b"] || ""}
onChange={handleChange}
/>
<button type="submit">Save</button>
</form>
</div>
);
}
You can do like: setState({ ...state, [name]: value }); it should work for object
You can make use of useEffect to do something after the state is updated.
const [state, setState] = useState({});
...
useEffect(() => {
console.log('Do something after state has changed', state);
}, [state]);
PS: This will run in the first render though.
You can take a look this code for your reference. https://codesandbox.io/s/sleepy-saha-q7h72
function App() {
const [state, setState] = React.useState({ firstName: "", lastName: "" });
const handleChange = e => {
setState({ ...state, [e.target.name]: e.target.value });
};
return (
<div className="App">
<input
type="text"
placeholder="first name"
onChange={handleChange}
value={state.firstName}
name="firstName"
/>
<br />
<input
type="text"
placeholder="last name"
onChange={handleChange}
value={state.lastName}
name="lastName"
/>
<hr />
{state.firstName}--{state.lastName}
</div>
);
}
Is there another way of getting/setting the values from the dom that is less expensive than useRef()? Is useRef() to be used lightly as the docs suggest?
import React, { useRef, useEffect } from 'react';
const Join: React.FC = () => {
const fullName = useRef<HTMLInputElement>(null);
const email = useRef<HTMLInputElement>(null);
const password = useRef<HTMLInputElement>(null);
const myForm = useRef<HTMLFormElement>(null);
useEffect(() => {
if (myForm.current) myForm.current.reset();
if (fullName.current) fullName.current.focus();
}, []);
return (
<div>
<form ref={myForm}>
<input type='text' ref={fullName} />
<input type='text' ref={email} />
<input type='text' ref={password} />
</form>
</div>
)
}
When the component loads I want to clear the form and focus the
fullName input
You don't need refs for that
I want to clear the form
Make your inputs controlled
Declare an empty string as initial value
const Component = () =>{
const [state, setState] = useState({
email : '',
password : ''
})
const onChange = ({ target: { value, name } }) =>{
setState(prev => ({
...prev,
[name] : value
}))
}
const { email, password } = state
return(
<>
<input value={email} onChange={onChange} id='email'/>
<input value={password} onChange={onChange} id='password' />
</>
)
}
Automatically focus a given input
Just use autofocus for that
<input autofocus/>