Why is only the last input value displayed on screen? - javascript

I'm having problem with my input fields. I got five of them. When i fill all of them and press the button to display them on the screen, only the last one I typed is displayed on all places... (this was hard to formulate, ask if you don't understand)
Here is when i want the output:
´´´
{this.state.new_array.map((char, i) => (
<li key={i}>
{`(${char.gender})
${char.name}
${char.height}
${char.eye_color}
${char.birth_year}`}
</li>
))}
´´´
Here's my input fields and button:
´´´
<input onChange={this.handleChange}></input>
<input onChange={this.handleChange}></input>
<input onChange={this.handleChange}></input>
<input onChange={this.handleChange}></input>
<input onChange={this.handleChange}></input>
<button onClick={this.addNewCharacter} }>Add character</button>
´´´
And here's my functions connected to button and inputs:
´´´
constructor(props){
super(props);
this.state = {
new_array: [],
nameFromInput: "",
heightFromInput: "",
birthyearFromInput: "",
genderFromInput: "",
eyecolorFromInput: ""
}
}
addNewCharacter = () => {
this.setState(oldState => ({
new_array: [...oldState.new_array, { // IM PRETTY SURE IT HAS TO DO WITH THIS FUCNTION?
name: this.state.nameFromInput,
height: this.state.heightFromInput,
birth_year: this.state.birthyearFromInput,
gender: this.state.genderFromInput,
eye_color: this.state.eyecolorFromInput
}]
}))
this.setState({showFavorites: true})
}
handleChange = event => {
this.setState({
nameFromInput: event.target.value,
heightFromInput: event.target.value,
birthyearFromInput: event.target.value,
genderFromInput: event.target.value,
eyecolorFromInput: event.target.value});
}
´´´

Why make something so easy complicated?
Here's a working sample in Codesandbox https://codesandbox.io/s/serene-hodgkin-d5kz9?file=/src/App.js
Solution posted below for brevity:
import React from "react";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
height: "",
birthyear: "",
gender: "",
eyecolor: ""
};
}
handleChange = event => {
this.setState({
name: event.target.value,
height: event.target.value,
birthyear: event.target.value,
gender: event.target.value,
eyecolor: event.target.value
});
};
addNewCharacter = () => {
const { name, height, birth_year, gender, eye_color } = this.state;
this.setState({
new_array: {
name,
height,
birth_year,
gender,
eye_color
},
showFavorites: true
});
console.log(this.state);
};
render() {
return (
<div className="App">
<input onChange={this.handleChange} />
<input onChange={this.handleChange} />
<input onChange={this.handleChange} />
<input onChange={this.handleChange} />
<input onChange={this.handleChange} />
<button onClick={this.addNewCharacter}>Add character</button>
</div>
);
}
}

Every time you change any of the input fields' values, you're calling this.handleChange, the same function every time, with that input change Event passed as an argument.
No matter which input this event is fired from, every key in the state gets updated with the value from that input.
Something that might look a little better is:
generateHandleChange = key => event => {
this.setState({[key]: event.target.value});
};
and your inputs would look like this:
<input onChange={this.generateHandleChange('name')} />
<input onChange={this.generateHandleChange('height')} />
<input onChange={this.generateHandleChange('birth_year')} />
<input onChange={this.generateHandleChange('gender')} />
<input onChange={this.generateHandleChange('eye_color')} />
<button onClick={this.addNewCharacter}>Add character</button>

Related

Why does my React form auto-refresh the page even if I put "event.preventDefault()" on handleSubmit?

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.

ReactJS: Why does my textarea value always render invisible?

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

Unable to type into React input field

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.

Single onChange function for multiple input fields is not working: ReactJS

Code:
import React, { Component } from 'react';
import { Button, Input, Row, Col, Label } from 'reactstrap';
export default class Settings extends Component {
constructor(props) {
super(props);
this.state = {
tallyPort: '', companyYear: '', interval: '', timeRange: '',
databasePort: '', databaseUserName: '', databasePassword: ''
};
}
handleChange = (stateName, e) => {
this.setState({ stateName: e.target.value });
}
handleSave = () => {
console.log(this.state)
}
render() {
return(
<div className="dashboard" >
<Input name="tallyPort" onChange={this.handleChange.bind(this, 'tallyPort')} />
<Input name="companyYear" onChange={this.handleChange.bind(this, 'companyYear')} />
<Input name="interval" onChange={this.handleChange.bind(this, 'companyYear')} />
<Input name="timeRange" onChange={this.handleChange.bind(this, 'companyYear')} />
<Input name="databasePort" onChange={this.handleChange.bind(this, 'companyYear')} />
<Input name="databaseUserName" onChange={this.handleChange.bind(this, 'companyYear')} />
<Input name="databasePassword" onChange={this.handleChange.bind(this, 'companyYear')} />
<Button style={{ width: '200px', marginLeft: '720px'}} onClick={this.handleSave.bind(this)} color="primary">Save</Button>
</div>
);
}
}
Main problem with this.setState function, I'm not understand why it is not working.
I'm trying to set each state value on "onChange" of input field, "setState" is not working properly, when i'm console all states after given values, it returns blank values, any one help me?
Basic Idea:
You need to use Computed property name concept, to use the expressions for object property names. For that you have to put the expression inside [].
Solution:
You are passing the state variable name in onChange function, so you need to use [] because it will be a variable that will hold some state variable name, then only it will update that state variable.
If you don't use [] then, stateName will be treated as a key (string), it will create a new state variable with name stateName and put the value in that.
Write it like this:
handleChange(stateName, e) {
this.setState({ [stateName]: e.target.value });
}
Check this:
let obj = {b: 5};
let a = 'b';
obj = {...obj, a: 20}; //similar to setState
obj = {...obj, [a]: 1};
console.log(obj);
As mentioned above the Computed property name is the key to this solution. This solution worked well for me.
constructor() {
super(props);
this.state = {tallyPort: '', companyYear: '', interval: '', timeRange: '',
databasePort: '', databaseUserName: '', databasePassword: ''};
this.handleChange = this.handleChange.bind(this);
}
// assigning the name and value is the main objective of this solution.
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return(
<div className="dashboard" >
<Input name="tallyPort" onChange={this.handleChange} />
<Input name="companyYear" onChange={this.handleChange} />
<Input name="interval" onChange={this.handleChange} />
<Input name="timeRange" onChange={this.handleChange} />
<Input name="databasePort" onChange={this.handleChange} />
<Input name="databaseUserName" onChange={this.handleChange} />
<Input name="databasePassword" onChange={this.handleChange} />
<Button style={{ width: '200px', marginLeft: '720px'}} onClick={this.handleSave.bind(this)} color="primary">Save</Button>
</div>
);
}
I hope this solution helps. Thanks.

Can't set React state with defaultValue

I'm trying to make the user profile editable in my component. Right now, when the user clicks "edit," the profile is replaced with a form that has the values they typed in as defaults. However, if they update only one field, the others get rewritten with blank values instead of passing the default value to the state.
Is there a way to pass the defaultValue to the state? I've tried value={} too but then the value doesn't change at all.
I'm trying to avoid having an "Edit" button for each input.
class AccountEditor extends Component {
constructor() {
super()
this.state = {
isEditing: false,
profile: {
firstName: '',
lastName: '',
city: '',
email: '',
bio: '',
}
}
}
toggleEdit(event) {
event.preventDefault()
this.setState({
isEditing: !this.state.isEditing
})
}
updateProfile(event) {
let updatedProfile = Object.assign({}, this.state.profile)
updatedProfile[event.target.id] = event.target.value
this.setState({
profile: updatedProfile
}
}
submitUpdate(event) {
event.preventDefault()
this.props.onUpdate(this.state.profile)
this.setState({
isEditing: !this.state.isEditing
})
}
render() {
let profile = this.props.profile
let content = null
if (this.state.isEditing == true) {
content = (
<div>
<input
id="firstName"
onChange={this.updateProfile.bind(this)}
defaultValue={profile.firstName} />
<br />
<input
id="lastName"
onChange={this.updateProfile.bind(this)}
defaultValue={profile.lastName} />
<br />
<input
id="city"
onChange={this.updateProfile.bind(this)}
defaultValue={profile.city} />
<br />
<input
id="email"
onChange={this.updateProfile.bind(this)}
defaultValue={profile.email} />
<br />
<textarea
id="bio"
onChange={this.updateProfile.bind(this)}
defaultValue={profile.bio} />
<br />
<button onClick={this.submitUpdate.bind(this)}>Done</button>
</div>
)
} else {
content = (
<div>
<h4>Name: </h4>
<span>{profile.firstName}</span>
<span>{profile.lastName}</span><br/>
<h4>City: </h4>
<span>{profile.city}</span><br/>
<h4>Bio :</h4>
<p>{profile.bio}</p><br />
<button onClick={this.toggleEdit.bind(this)}>Edit</button>
</div>
)
}
return (
<div>
{content}
</div>
)
}
}
export default AccountEditor
You should replace defaultValue with value = { this.state.someProp }. So an example with your code would be
constructor(props) {
super(props)
this.state = {
isEditing: false,
profile: props.profile // Setting up the initial data from the passed prop
}
}
and
<input id="firstName"
onChange={ this.updateProfile.bind(this) }
value={ this.state.profile.firstName } />
More about using react with form elements in these docs.

Categories

Resources