Saving object in React.useState - reactjs

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.

Related

I can't find an example of how to make an HTTP POST request using useEffect hook along with axios

Lately, I've been trying to make an HTTP POST request using the useEFfect hook along with axios, but the code returns this error in the console: Error: Request failed with status code 422. The POST request is always successfully made when I make it from the form onSubmit handler. However, when I try to make it from the useEffect hook, it gives that error. Can anybody help me with what I'm doing wrong? Here's the code:
import React, { useState, useEffect } from "react";
import axios from "axios";
function RandomPost() {
const [input, setInput] = useState({
name: "",
email: "",
companyName: "",
password: "",
});
const handleChange = (event) => {
setInput({ ...input, [event.target.id]: event.target.value });
};
const handleSubmit = (e) => {
e.preventDefault();
};
useEffect(() => {
const { name, email, companyName, password } = input;
axios
.post(`https://01hire.collaq.com/user/createCompany`, input)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
}, [input]);
const { name, email, companyName, password } = input;
return (
<div>
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" id="name" value={name} onChange={handleChange} />
</label>
<label>
Email:
<input type="text" id="email" value={email} onChange={handleChange} />
</label>
<label>
Company Name:
<input
type="text"
id="companyName"
value={companyName}
onChange={handleChange}
/>
</label>
<label>
Password:
<input
type="password"
id="password"
value={password}
onChange={handleChange}
/>
</label>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default RandomPost;

React form with useState for multiple values

I have a demo here
It's a simple react form with typescript
Is it possible to use useState to capture the input fields data when the fields are completed
The form isn't just one piece of data but 3 - username, password and email
I could do it for one value with
onChange={e => setContact(e.target.value)}
but can I do it for 3 separate values
You can do it using computed properties.
First add a name property to each of your input, with values "username", "password", "email", then:
onChange={e => setContact(contact => ({...contact, [e.target.name]: e.target.value}))}
Edit: in React versions before 17, events are pooled and setContact update function is running asynchronously, so the event needs to be persisted this way:
onChange={e => {
e.persist();
setContact(contact => ({...contact, [e.target.name]: e.target.value}));
}}
Your handler could refer to the input name which you could then use to update the form object. There's a couple of issues with your syntax for this on the stackblitz - i.e the state object should be an object, not an array, so here's a full example:
const App = () => {
interface IForm {
username: string;
password: string;
email: string;
}
const [form, setFormValue] = useState<IForm>({
username: "",
password: "",
email: ""
});
const updateForm = (
formKey: keyof IForm,
event: React.ChangeEvent<HTMLInputElement>
) => {
const { value } = event.target;
setFormValue({
...form,
[formKey]: value
});
};
const handelSubmit = () => {
console.log("Form Submitted! Values: ", form);
};
return (
<div>
<form onSubmit={handelSubmit}>
<div>
<input
type="text"
placeholder="username"
value={form.username}
onChange={e => updateForm("username", e)}
/>
</div>
<div>
<input
type="password"
placeholder="password"
value={form.password}
onChange={e => updateForm("password", e)}
/>
</div>
<div>
<input
type="email"
placeholder="email"
value={form.email}
onChange={e => updateForm("email", e)}
/>
</div>
<input className="submit" type="submit" value="submit" />
</form>
</div>
);
};
You can create a object in the state and update the state with cloned object.
Here you go, Code with results as you expect:
import React, { useState } from "react";
import { render } from "react-dom";
import './style.css'
const App = () => {
interface IFrom {
username: string;
password: string;
email: string;
}
const [contact, setContact] = useState<IFrom[]>({
username:"",
email:"",
password:"",
});
console.log(contact)
const handelSubmit = () =>
{
axios.post(contact) //Example
};
return (
<div>
<form onSubmit={handelSubmit}>
<div>
<input
type="text"
placeholder="username"
value={contact.usename}
onChange={e => setContact({...contact, username: e.target.value})}
/>
</div>
<div>
<input
type="password"
placeholder="password"
onChange={e => setContact({...contact, password: e.target.value})}
/>
</div>
<div>
<input
type="email"
placeholder="email"
onChange={e => setContact({...contact, email: e.target.value})}
/>
</div>
<input className='submit' type="submi" value='submit'/>
</form>
</div>
);
};
render(<App />, document.getElementById("root"));

React Input Warning: A component is changing a controlled input of type text to be uncontrolled

I am practicing REST API by using one Fake API site. For front-end, I am using React typescript and React router dom for routing. I successfully login the email and password by using Fake API's login and redirect to list users, where I fetched the data from Fake API and shows the user's name, image. I used the edit button, after clicking the button it will redirect to my Update components where it will populate the input field then I will update the data. My update components work fine as expected but in my console, I am getting a warning as soon as I type my input field.Here is the Error visualization
This is React Update components
import React, { useState, useEffect } from "react";
import axios from "axios";
const Update = props => {
const [state, setState] = useState({
first_name: "",
last_name: "",
email: ""
});
const [loading, setLoading] = useState(false);
useEffect(() => {
axios
.get("https://reqres.in/api/users/" + props.match.params.id)
.then(response => {
setState({
first_name: response.data.data.first_name,
last_name: response.data.data.last_name,
email: response.data.data.email
});
})
.catch(function(error) {
console.log(error);
});
}, [props.match.params.id]);
const onChangeFirstName = e => {
setState({
first_name: e.target.value
});
};
const onChangeLastName = e => {
setState({
last_name: e.target.value
});
};
const onChangeEmail = e => {
setState({
email: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
setLoading(true);
const obj = {
first_name: state.first_name,
last_name: state.last_name,
email: state.email
};
axios
.patch("https://reqres.in/api/users/" + props.match.params.id, obj)
.then(res => console.log(res.data));
setLoading(false);
props.history.push("/users");
};
return (
<div>
<form onSubmit={onSubmit}>
<div className="form-group">
<label>First Name: </label>
<input
type="text"
className="form-control"
value={state.first_name}
onChange={onChangeFirstName}
id="first_name"
/>
</div>
<div className="form-group">
<label>Last Name: </label>
<input
type="text"
className="form-control"
value={state.last_name}
onChange={onChangeLastName}
id="last_name"
/>
</div>
<div className="form-group">
<label>Email: </label>
<input
type="email"
className="form-control"
value={state.email}
onChange={onChangeEmail}
id="email"
/>
</div>
<div className="form-group">
<button
className="btn waves-effect blue lighten-1"
type="submit"
name="action"
disabled={loading}
>
{loading ? "loading..." : "save"}
</button>
</div>
</form>
</div>
);
};
export default Update;
With hooks, when you set the state of an object, you need to merge all the properties by yourself. In other words, if you update a property of an object with state updater, the remaining properties of the objects are not merged by themselves unlike this.setState in class components.
Modify your onChange to like this:
const onChangeFirstName = e => {
const val = e.target.value;
setState(prevState => ({
...prevState,
first_name: val
}));
};
See working demo
Also quick suggestion:
Instead of writing multiple onChanges, you can simplify and just use one.
Like this:
<input
type="text"
className="form-control"
value={state.first_name}
onChange={onChange}
id="first_name"
name="first_name" />
...
const onChange = e => {
const {name, value} = e.target;
setState(prevState => ({
...prevState,
[name]: value
}));
};

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

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

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

Resources