How to use the updated state after setState call with react-hook - reactjs

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>
);
}

Related

React State Usage in New Version

I am new to react,
Currently we are using new version of react,
The new version of react state (Current Version):
class MyForm extends React.Component {
const [state, setState] = useState([]);
myChangeHandler = (event) => {
let nam = event.target.name;
let val = event.target.value;
setState({[nam]: val});
}
render() {
return (
<form>
<h1>Hello {state.username} {state.age}</h1>
<p>Enter your name:</p>
<input
type='text'
name='username'
onChange={myChangeHandler}
/>
<p>Enter your age:</p>
<input
type='text'
name='age'
onChange={myChangeHandler}
/>
</form>
);
}
}
How to i define it in the old React.
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
age: null,
};
}
myChangeHandler = (event) => {
let nam = event.target.name;
let val = event.target.value;
this.setState({[nam]: val});
}
render() {
return (
<form>
<h1>Hello {this.state.username} {this.state.age}</h1>
<p>Enter your name:</p>
<input
type='text'
name='username'
onChange={this.myChangeHandler}
/>
<p>Enter your age:</p>
<input
type='text'
name='age'
onChange={myChangeHandler}
/>
</form>
);
}
}
I have used it similar to the old one but the age value in state is cleared when the name state is updated and viceversa
Issues
React hooks are only valid in functional components.
You are using an array for state when it's an object in old code.
Unlike class-based component's this.setState state updates are not shallowly merged, you need to manage this yourself.
There is no render method, the entire functional component body is the "render" function. Just return the JSX.
Solution
const MyForm = () => {
const [state, setState] = useState({ // <-- use object
age: null,
username: '',
});
const myChangeHandler = (event) => {
const { name, value } = event.target;
setState(state => ({
...state, // <-- copy previous state
[name]: val, // <-- update property
}));
}
return (
<form>
<h1>Hello {state.username} {state.age}</h1>
<p>Enter your name:</p>
<input
type='text'
name='username'
onChange={myChangeHandler}
/>
<p>Enter your age:</p>
<input
type='text'
name='age'
onChange={myChangeHandler}
/>
</form>
);
}
function MyForm() {
const [name, setName] = useState('');
const [age, setAge] = useState(0);
return (
<form>
<h1>Hello {state.username} {state.age}</h1>
<p>Enter your name:</p>
<input
type='text'
name='username'
onChange={(event) => setName(event.target.value)}
/>
<p>Enter your age:</p>
<input
type='text'
name='age'
onChange={(event) => setAge(event.target.value)}
/>
</form>
);
}

How to pass the value of checkbox in react?

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;

Saving object in React.useState

I have a form in my react project, and I want the value of each field to be stored in the state.
instead of having multiple states for each field, how can I store the form value as an object in the state? and more importantly how can I access it? (with react hooks)
import React from 'react';
export const UserData = () => {
return(
<form>
<input type="text" placeholder="Name" />
<input type="email" placeholder="Email" />
<button>Confirm</button>
</form>
)
}
React Hooks allows you to define a JavaScript Object using useState. See
https://daveceddia.com/usestate-hook-examples/
function LoginForm() {
const [form, setState] = useState({
username: '',
password: ''
});
Update the form using the function :
const updateField = e => {
setState({
...form,
[e.target.name]: e.target.value
});
};
Call the function onSubmit of the button
<form onSubmit={updateField}>
<input type="text" placeholder="Name" />
<input type="email" placeholder="Email" />
<Button >Confirm</button>
</form>
You can use useState hook. Check this
const [state, setState] = useState({ name, email });
To set the state similar to setState in class based component:
setState({name: 'react', email: 'react'})
To access the state value:
import React, { useState } from 'react';
export const UserData = () => {
const [state, setState] = useState({ name, email });
return(
<form>
<input type="text" placeholder="Name" value={state.name} />
<input type="email" placeholder="Email" value={state.email} />
<button>Confirm</button>
</form>
)
}
You should create an initial state value if you want to store the form values as an object using useState, so you can rollback to the initial state after an error. Example,
const initialState = {
name: "",
email: ""
};
export const UserData = () => {
const [formState, setFormState] = useState(initialState);
const submitHandler = event => {
event.preventDefault();
console.log(formState);
};
return (
<form onSubmit={submitHandler}>
<input
type="text"
placeholder="Name"
value={formState.name}
onChange={e => {
setFormState({ ...formState, name: e.target.value });
}}
/>
<input
type="email"
placeholder="Email"
value={formState.email}
onChange={e => {
setFormState({ ...formState, email: e.target.value });
}}
/>
<button>Confirm</button>
</form>
);
};
Working demo in codesandbox.

React TypeScript: Alternatives to UseRef in functional components

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/>

Handle an input with React hooks

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

Resources