I am working in ReactJS on post API. I have given a task to create some text fields in which I have to give some values and then on click of a submit button the information from text field will be submitted to POST API which I have taken from JasonPlaceHolder.
I have written a code which is given at last. Whenever I click Register button the value in last input field (which in my case is "EMAIL") overrides the values of all the input fields.
A screenshot of my problem is also attached:
Code
import React from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
class App extends React.Component {
constructor(props){
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
inter:0,
ID: '',
Name: '',
username : '',
Email: ''
};
this.updateInput = this.updateInput.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
updateInput(event){
this.setState({ID : event.target.value})
this.setState({Name : event.target.value})
this.setState({username : event.target.value})
this.setState({Email : event.target.value})
}
handleSubmit(){
var dict = { id: this.state.ID, name:this.state.Name , username: this.state.username , email: this.state.Email };
axios.post('https://jsonplaceholder.typicode.com/users', dict)
.then(response => {
console.log(response)
this.setState({isLoaded:true,
flag:1,
})
})
.catch(error => {
alert(error);
console.log(error);
})
}
render() {
const { error, isLoaded, items } = this.state;
if (this.state.inter == 0){
return(
<div>
<form>
<p>ID:<input type="text" name="ID" onChange={this.updateInput}/> </p>
<p>Name:<input type="text" name="name" onChange={this.updateInput} /> </p>
<p>User Name:<input type="text" name="username" onChange={this.updateInput} /></p>
<p>Email: <input type="text" name="email" onChange={this.updateInput} /></p>
</form>
<button onClick={this.handleSubmit}>
Register
</button>
</div>
);
}else{
return(
<div>
<button onClick={this.handleSubmit} >
Register
</button>
<h1> Post Request Submitted Successfully</h1>
</div>
);
}
}
componentDidMount() {
}
}
export default App;
Your updateInput method overrides all the other fields, that's why the last updated field (the email) is the one you are seeing on the request payload.
To solve this you can separate the updateInput method for each input field or inside updateInput check which field you're currently updating and update only that field.
You can check here for more details:
https://medium.com/#zacjones/handle-multiple-inputs-in-react-with-es6-computed-property-name-e3d41861ae46
You have to update the fields like this,
updateInput({target: {name, value}}){
this.setState({[name]: value});
}
The issue is in the updateInput method. change it to.
const { name, value } = event.target;
this.setState(state => ({
...state, [name]: value,
}));
N:B You will have to make the names of the input element same as the state field.
Change your updateInput logic to this.
updateInput(event){
this.setState({[event.target.name] : event.target.value})
}
Related
I do this project from this course and I want to increase the possibilities of the app by giving the user the ability to insert a birthday person by putting the name, age and photo with a HTML form. In order to do so i use this:
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
class MyForm extends React.Component {
constructor(props) {
super(props)
this.state = {
username: '',
age: null,
filename: null,
}
}
myChangeHandler = (event) => {
event.nativeEvent.stopImmediatePropagation()
let nam = event.target.name
let val = event.target.value
console.log('click')
if (nam === 'age') {
if (!Number(val)) {
alert('Your age must be a number')
}
}
this.setState({
[nam]: val,
file: URL.createObjectURL(event.target.files[0]),
})
}
render() {
return (
<div className="form">
<form>
<h1 className="title">
Today is the Birtday of {this.state.username} of {this.state.age}{' '}
Years Old
</h1>
<img src={this.state.file} alt="Birtday Pic" />
<p>Enter the name of the Birtday Person:</p>
<input type="text" name="username" onChange={this.myChangeHandler} />
<p>Enter his/her age:</p>
<input type="text" name="age" onChange={this.myChangeHandler} />
<p>Click on the chosen button to send the birtday pic</p>
<input
type="file"
id="myFile"
name="filename"
onChange={this.myChangeHandler}
></input>
<button
type="button"
onClick={() => document.getElementById('myFile').click()}
className="send"
>
Send the Birtday Picture
</button>
<input type="submit" value="Submit"></input>
</form>
</div>
)
}
}
export default MyForm
But here is the issue,I can insert the image but when I tring to put name or age this happends:
I am just a rookie with React, maybe this is some silly thing, maybe I don't use well setState(), but I can't see the issue right now
You're using the same setState for every change:
this.setState({
[nam]: val,
file: URL.createObjectURL(event.target.files[0]),
})
HOWEVER, not every event will have .files - only the change handler used by the file input.
I would make a separate change handler for the file input. Have one for text inputs, and one for file inputs.
For example, for file inputs:
myFileChangeHandler = (event) => {
event.nativeEvent.stopImmediatePropagation()
let nam = event.target.name
let val = event.target.value
this.setState({
[nam]: val,
file: URL.createObjectURL(event.target.files[0]),
})
}
and this for text inputs:
myTextChangeHandler = (event) => {
event.nativeEvent.stopImmediatePropagation()
let nam = event.target.name
let val = event.target.value
if (nam === 'age') {
if (!Number(val)) {
alert('Your age must be a number')
}
}
this.setState({
[nam]: val,
})
}
I have two files which work together to render things. The first is App.js, which first renders Form.js. The form will then collect information, which on submission, changes the Form state and calls a function from App.js. This function is called "createProject." Calling "createProject" in Form.js "handleSubmit" makes the page auto-refresh. However, if I remove "createProject" from handleSubmit, the page does not auto-refresh. Here are the two files.
import React, { Component } from "react";
import Project from "./components/Project.js"
import Form from "./Form.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
projectList: [],
myProjects: [],
userList: [],
submitted: false
};
this.createProject = this.createProject.bind(this);
}
createProject(title, desc, langs, len, exp) {
this.setState({
projectList: this.state.projectList.push([
{
title : title,
description : desc,
language : langs,
length : len,
experience : exp
}
]),
submitted : true
});
}
deleteProject(title) {
const projects = this.state.projectList.filter(
p => p.title !== title
);
this.setState({projects});
}
render() {
let info;
if (this.state.submitted) {
info = (
<div>
<p>cccc</p>
</div>
);
} else {
info = (
<br/>
);
}
return(
<div>
<Form/>
{info}
{this.state.projectList.map((params) =>
<Project {...params}/>)}
</div>
);
}
}
export default App;
import React from "react";
import createProject from "./App.js"
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "",
description: "",
language: "",
length: 0,
experience: "",
submitted: false
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleSubmit(event) {
this.setState({
submitted: true
})
createProject(
this.state.title,
this.state.description,
this.state.language,
this.state.length,
this.state.experience
)
event.preventDefault();
}
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
let info;
if (this.state.submitted) {
info = (
<div>
<h1>{this.state.title}</h1>
<p>{this.state.description}</p>
<p>{this.state.language}</p>
<p>{this.state.length}</p>
<p>{this.state.experience}</p>
</div>
);
} else {
info = <br/>;
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Title:
<input
name="title"
type="textbox"
checked={this.state.title}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Description:
<input
name="description"
type="textbox"
checked={this.state.description}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Language:
<input
name="language"
type="textbox"
checked={this.state.language}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Length:
<input
name="length"
type="number"
checked={this.state.length}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Experience:
<input
name="experience"
type="textbox"
checked={this.state.experience}
onChange={this.handleInputChange} />
</label>
<br />
<input type="submit" value="Submit" />
</form>
{info}
</div>
);
}
}
export default Form;
I've also tried adding "new" to the "createProject" in handleSubmit, and while that does stop the auto-refresh, it will not call the createProject function. (Or maybe it does, but none of the code in the createProject function seems to be run.) Can anyone help with preventing this auto refresh while also allowing App's createProject function to run properly?
The page auto refreshes because execution never gets to your event.PreventDefault() line. This is due to an error encountered when react tries to evaluate createProject. To fix this, correct handleSubmit like so.
handleSubmit(event) {
event.preventDefault(); // moved up in execution.
this.setState({
submitted: true
})
createProject(
this.state.title,
this.state.description,
this.state.language,
this.state.length,
this.state.experience
)
}
Notice that moving event.PreventDefault() to the top of your handleSubmit(event) function just before this.setState line prevents default form behaviour on submit.
You however get an error because App.js doesn't export a function called createProject. To maintain the createProject within App instance, you need to pass it as a prop which you can then reference as this.props.createProject.
See this answer on how to do call a Parent method in ReactJS.
I'm making an edit form which inside Modal. Form inputs has defaultValue that comes from props with an name, age and strength as default value. If I change all inputs value, everything works fine, but if I change only one input value when I console.log payload I got and empty strings, but only value that I changed goes to the payload. How can I make that even I change only one input payload gets inputs default value from props?
My code is here:
import React, { Component } from 'react';
import './Form.scss';
import axios from 'axios';
class Form extends Component {
constructor(props){
super(props);
this.state = {
id: this.props.gnomeId,
submitedName: '',
submitedAge: '',
submitedStrength: ''
}
this.onSubmit = this.onSubmit.bind(this);
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
});
}
onSubmit(event) {
event.preventDefault();
const gnome = {
name: this.state.submitedName,
age: this.state.submitedAge,
strenght: this.state.submitedStrength,
}
console.log(gnome);
axios.post(`${API}/${this.state.id}`, {gnome})
.then( res => {
console.log(res);
console.log(res.data);
});
}
render() {
return(
<form onSubmit={this.onSubmit}>
<div>
<label htmlFor="name">Name</label>
<input type="text" name="submitedName" id="name" defaultValue={this.props.gnomeName} onChange={this.handleChange}/>
</div>
<div>
<label htmlFor="age">Age</label>
<input type="text" name="submitedAge" id="age" defaultValue={this.props.gnomeAge} onChange={this.handleChange}/>
</div>
<div>
<label htmlFor="strength">Strength</label>
<input type="text" name="submitedStrength" id="strength" defaultValue={this.props.gnomeStrength} onChange={this.handleChange}/>
</div>
<button type="submit" className="submit-btn">Submit</button>
</form>
)
}
}
export default Form;
Just set the initial state to the default values:
super(props);
this.state = {
id: this.props.gnomeId,
submitedName: props.gnomeName,
submitedAge: props.gnomeAge,
submitedStrength: props.gnomeStrength
}
You may use Nullish_Coalescing_Operator to give different value from init as null/undefined
this.state = {
submitedName: null,
...
}
const gnome = {
name: this.state.submitedName ?? this.props.submitedName,
...
}
First remove the defaultProps.
add componentDidUpdate in class based component and useEffect in function based component
componentDidUpdate(props) {
if(props){
this.state = {
id: this.props.gnomeId,
submitedName: props.gnomeName,
submitedAge: props.gnomeAge,
submitedStrength: props.gnomeStrength
}
}
}
it is necessary to put props in if because it has undefined initial so not to get error
Trying to set up something simple.
Parent: app.js
class App extends React.Component {
constructor(props) {
super(props);
//This acts as our global state
this.state = {
username: "",
email: "",
bio: ""
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
username: "jonny",
email: "jonny#mail.com",
bio: "My bio...."
});
}, 5000);
}
handleFormChange = data => {
this.setState(data);
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Form data={this.state} onHandleFormChange={this.handleFormChange} />
<p>Name from App state: {this.state.username}</p>
<p>Email from App state: {this.state.email}</p>
<p>Bio from App state: {this.state.bio}</p>
</div>
);
}
}
Child: form.js
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
...this.props.data
};
}
handleSubmit = e => {
e.preventDefault();
};
handleChange = e => {
this.props.onHandleFormChange({ [e.target.name]: e.target.value });
};
// static getDerivedStateFromProps(nextProps, prevState) {
// console.log(nextProps.data)
// return {
// ...nextProps.data
// };
// }
componentDidUpdate(prevProps) {
if (prevProps.data !== this.props.data) {
this.setState({ ...this.props.data });
}
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="username"
defaultValue={this.state.username}
onChange={this.handleChange}
/>
<input
type="email"
name="email"
defaultValue={this.state.email}
onChange={this.handleChange}
/>
<textarea
name="bio"
defaultValue={this.state.bio}
onChange={this.handleChange}
/>
<input type="submit" value="submit" />
</form>
</div>
);
}
}
I created an artificial API call by using a setTimeout() in this example and I'm trying to set the state of the parent with the result of the API call. Then I wish to pass that as a prop to the child...
It's working except in the case of a textarea. I can see it if I inspect the DOM but it doesn't actually show in the browser...
Note the "my bio..." in the inspector, but the textarea being empty in the browser.
I've tried componentWillUpdate(), componentDidUpdate() and getDerivedStateFromProps() but nothing seems to work.
What am I missing?
Note: I am not using value="" because then it stops me typing and this form is supposed to allow you to update existing values
Sandbox... https://codesandbox.io/s/ancient-cloud-b5qkp?fontsize=14
It seems to work fine by using the value attribute instead of defaultValue. The defaultValue attribute should really only be used sparingly, since you almost always want your inputs to connect to component state. The optimal way to create a controlled input is by using value.
<textarea
name="bio"
value={this.state.bio}
onChange={this.handleChange}
/>
Change the defaultValue in textarea to value
I am unable to type any input into my input field. I am using React, and have already set a handleChange and a handleSubmit function. The first two input fields, for 'name' and 'email', take input just fine. But for 'favoriteCity', it doesn't seem to work.
I am wondering if it is due to a MongoDB error that I am getting.
class UserPage extends Component {
state = {
user: [],
newUser: {
name: '',
email: '',
favoriteCity: ''
}
}
getAllUsers = () => {
axios.get('/api/users')
.then(res => {
this.setState({ user: res.data })
})
}
componentDidMount() {
this.getAllUsers()
}
handleChange = event => {
const newUser = { ...this.state.newUser };
newUser[event.target.name] = event.target.value;
this.setState({ newUser: newUser});
}
handleSubmit = event => {
event.preventDefault()
axios.post('/api/users', this.state.newUser)
.then(res => {
this.props.history.push(`/users/${res.data._id}`)
})
}
render() {
return (
<div>
{ /* This shows a list of All Users */ }
{this.state.user.map(user => (
<div key={user._id}>
<Link to={`/users/${user._id}`}>{user.name}</Link>
</div>
))}
<h1>New User Page</h1>
<form onSubmit={this.handleSubmit}>
<label>Name: </label>
<input
type="text"
name="name"
placeholder="Name?"
value={this.state.newUser.name}
onChange={this.handleChange}
/>
<label>Email: </label>
<input
type="text"
name="email"
placeholder="Email?"
value={this.state.newUser.email}
onChange={this.handleChange}
/>
<label>Favorite City: </label>
<input
type="text"
name="city"
placeholder="Favorite City?"
value={this.state.newUser.favoriteCity}
onChange={this.handleChange}
/>
<Button
type="submit"
value="Submit"
variant="contained"
color="primary"
>
Create User
</Button>
</form>
</div>
);
}
}
export default UserPage;
Please help.
Weird that email works fine, from what you posted your handleChange function is only updating the name on the newUser.
What you should see is what you type in all the inputs appear in the name input.
To fix this, you should probably have separate change handlers for each input:
handleNameChange
handleEmailChange
...
You should also consider storing name, email etc.. at the root of your state instead of nesting them in an object, that'll simplify the handler functions code.