friends, I am a React beginner, and I have a problem with passing data between components using props
I have three components Users, UserList, and AddUser. In the UserList component, all the users should be displayed and AddUser component contains a form. I want to grab data from the forms and update my state in Users component. My pages are Home, User List and Add New User
import React, { Component } from "react";
import UserList from "./userList";
class Users extends Component {
state = {
users: [
{
name: "ali",
lastname: "ahmadi",
language: "python",
gender: "male",
score: 100,
},
{
name: "Ahmad",
lastname: "moradi",
language: "JavaScript",
gender: "male",
score: 90,
},
],
};
removeUser = (name) => {
let users = this.state.users.filter((user) => {
return user.name !== name;
});
this.setState({
users,
});
};
render() {
return (
<div>
<UserList users={this.state.users} removeUser={this.removeUser} />
</div>
);
}
}
export default Users;
and my user list components code
import React from "react";
import Users from "./user";
const UserList = (props) => {
const { users } = props;
console.log(users);
return (
<div className="container">
<h1 className="text-center mt-4" style={{ fontFamily: "tahoma" }}>
All Users
</h1>
</div>
);
};
export default UserList;
here is my form component
import React, { Component } from "react";
import Users from "./user";
class AddUser extends Component {
state = {
name: "",
lastname: "",
language: "",
score: 0,
gender: "",
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value,
gender: e.target.value,
});
};
handleSubmit = (e) => {
e.preventDefault();
};
render() {
return (
<div className="container">
<form onSubmit={this.handleSubmit} className="col-lg-8 m-auto">
<fieldset className="form-group">
<legend>Add New User</legend>
<div className="form-group ">
<input
type="text"
className="form-control"
id="name"
placeholder="Name"
onChange={this.handleChange}
/>
<input
type="text"
className="form-control my-2"
id="lastname"
placeholder="Last Name"
onChange={this.handleChange}
/>
<input
type="text"
className="form-control my-2"
id="language"
placeholder="Language"
onChange={this.handleChange}
/>
<input
type="number"
className="form-control my-2"
id="score"
placeholder="Your Score"
onChange={this.handleChange}
/>
<div className="form-check form-check-inline">
<label className="form-check-label" htmlFor="inlineRadio1">
<b style={{ position: "relative", top: -2 }}> Male</b>
</label>
<input
className="form-check-input mx-2"
type="radio"
value="male"
name="gender"
onChange={this.handleChange}
></input>
</div>
<div className="form-check form-check-inline">
<label className="form-check-label" htmlFor="inlineRadio1">
<b style={{ position: "relative", top: -2 }}> Female</b>
</label>
<input
className="form-check-input mx-2"
type="radio"
name="gender"
value="female"
onChange={this.handleChange}
></input>
</div>
<br />
<button className="btn btn-outline-info btn-md mt-2">Add</button>
</div>
</fieldset>
</form>
</div>
);
}
}
export default AddUser;
it prints undefined I don't know what's the problem
You can pass the data through props and get the data from child.
getDataFromUserList() is a props that will recieve the value from the child.
constructor(props){
super(props);
this.state = {
users: [
{
name: "ali",
lastname: "ahmadi",
language: "python",
gender: "male",
score: 100,
},
{
name: "Ahmad",
lastname: "moradi",
language: "JavaScript",
gender: "male",
score: 90,
},
],
};
this.getDataFromUserList = this.getDataFromUserList.bind(this)
}
getDataFromUserList(data){
// Will get the value in data from the child
console.log(data);
}
<UserList users={this.state.users} removeUser={this.removeUser} getDataFromUserList={this.getDataFromUserList}/>
In the userlist page the function in user getDataFromUserList will get as a props.This props is again passed AddUser component as props name getDataFromAddUser
const UserList = (props) => {
const { users } = props;
console.log(users);
return (
<div className="container">
<h1 className="text-center mt-4" style={{ fontFamily: "tahoma" }}>
All Users
</h1>
<AddUser getDataFromAddUser={(data) => { props.getDataFromUserList(data)}} />
</div>
);
};
export default UserList;
In the AddUser component the state value set from the form is passed through userlist component using props.From the userlist component the data is passed to the user. You will get the value in addUser at the user component at getDataFromUserList() function
<button className="btn btn-outline-info btn-md mt-2" onClick={() => { this.props.getDataFromAddUser(this.state)}}>Add</button>
You have a typo here,
import UserList from "./userList";
It should be
import UserList from "./UserList";
Related
I want to add an item to a react state dictionary. Every time i press to submit i got the value is undefined on the dictionary object and it is append to the dictonary with a null values in name and cost but id is working fine. I find the issue but i am unable to find the solution. TIA.
import React, { Component } from "react";
import ExpenseItem from "./ExpenseItem.js";
class ExpensesList extends Component {
state = {
expenses: [{id: '12345' ,name: 'Pizza', cost: '20'}],
};
handleChange=(event)=>{
this.setState({[event.target.name]:event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
this.setState({expenses: [this.state.expenses,...[{id: Math.random(),name:this.state.expenses.name, cost:this.state.expense.cost}]]});
}
render() {
return (
<div>
<ul className="list-group">
{this.state.expenses.map((expense) => (
<ExpenseItem
id={expense.id}
name={expense.name}
cost={expense.cost}
/>
))}
</ul>
<div className ="row mt-3">
<h2> Add Expenses </h2>
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="form-group">
<label for="name">Name</label>
<input
required="required"
type="text"
className="form-control"
id="name"
value = {this.state.expenses.name}
onChange = {this.handleChange}
></input>
</div>
<div className="form-group">
<label for="name">Cost</label>
<input
required="required"
type="text"
className="form-control"
id="cost"
value = {this.state.expenses.cost}
onChange = {this.handleChange}
></input>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary"> Add Expense</button>
</div>
</div>
</form>
</div>
</div>
);
}
}
export default ExpensesList;
The issues is that this.state.expenses is an array. So, this.state.expenses.name and this.state.expenses.cost are both undefined.
If you want to be able to add new expense objects to the expenses array in state, you need a way to manage the new inputs in state. So, your state should look something like this:
state = {
expenses: [{id: '12345' ,name: 'Pizza', cost: '20'}],
cost: "",
name: ""
};
When a user inputs a cost and name, this.state.cost and this.state.name should be set in state, and then when the user clicks 'submit', a new object can be added to the this.state.expenses array.
(Also, your handleChange needs to specify which properties of state it intends to udpate)
Your final solution should look something like this:
import React, { Component } from "react";
import ExpenseItem from "./ExpenseItem.js";
class ExpensesList extends Component {
state = {
expenses: [{ id: '12345', name: 'Pizza', cost: '20' }],
cost: "",
name: ""
};
handleChange = (event, name) => {
this.setState({ [name]: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault();
let newExpense = {
id: Math.random(),
name: this.state.name,
cost: this.state.cost
}
this.setState(prevState => ({
expenses: [...prevState.expenses, newExpense], // add new expense to expenses array
cost: "", // reset this field
name: "" // reset this field
}));
}
render() {
return (
<div>
<ul className="list-group">
{this.state.expenses.map((expense) => (
<ExpenseItem
id={expense.id}
name={expense.name}
cost={expense.cost}
/>
))}
</ul>
<div className="row mt-3">
<h2> Add Expenses </h2>
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="form-group">
<label for="name">Name</label>
<input
required="required"
type="text"
className="form-control"
id="name"
value={this.state.name}
onChange={e => this.handleChange("name")}
></input>
</div>
<div className="form-group">
<label for="name">Cost</label>
<input
required="required"
type="text"
className="form-control"
id="cost"
value={this.state.cost}
onChange={e => this.handleChange("cost")}
></input>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary"> Add Expense</button>
</div>
</div>
</form>
</div>
</div>
);
}
}
export default ExpensesList;
I am following along with this tutorial (https://medium.com/#beaucarnes/learn-the-mern-stack-by-building-an-exercise-tracker-mern-tutorial-59c13c1237a1) and am trying to print to console through the onSubmit function but this is not occurring and I do not know why. I've made sure to use the bind method, and everything seems to be referenced correctly. What am I missing here?
import React, {Component} from 'react';
import DatePicker from 'react-datepicker';
import "react-datepicker/dist/react-datepicker.css";
export default class CreateWorkout extends Component {
constructor(props) {
super(props); // Refers to the parent class constructorq
this.onChangeUsername = this.onChangeUsername.bind(this);
this.onChangeDescription = this.onChangeDescription.bind(this);
this.onChangeReps = this.onChangeReps.bind(this);
this.onChangeDuration = this.onChangeDuration.bind(this);
this.onChangeDate = this.onChangeDate.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
username:'',
description: '',
reps: 0,
duration: 0,
date: new Date(),
users: []
}
}
componentDidMount() { // Invoked immediately after a component is mounted
this.setState({
users: ['test user'],
username: 'test user'
});
}
onChangeUsername(e) {
this.setState({
username: e.target.value
});
}
onChangeDescription(e) {
this.setState({
description: e.target.value
});
}
onChangeReps(e) {
this.setState({
reps: e.target.value
});
}
onChangeDuration(e) {
this.setState({
duration: e.target.value
});
}
onChangeDate(date) { // Need to add a date picker library for this
this.setState({
date: date
});
}
// Submit method for submitting an event of the form...
onSubmit(e) {
e.preventDefault();
const workout = {
username: this.state.username,
description: this.state.description,
reps: this.state.reps,
duration: this.state.duration,
date: this.state.date,
};
console.log(workout);
window.location = '/'; // After form is submitted, the location is updated so the user is taken back to the home page
}
render() {
return (
<div>
<h3>Create New Workout Log</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Username: </label>
<select ref="userInput" required className="form-control" value={this.state.username} onChange={this.onChangeUsername}>
{
this.state.users.map(function(user) {
return <option key={user} value={user}>{user}</option>;
})
}
</select>
</div>
<div className="form-group">
<label>Description: </label>
<input type="text" required className="form-control" value={this.state.description} onChange={this.onChangeDescription}/>
</div>
<div className="form-group">
<label>Reps: </label>
<input type="text" className="form-control" value={this.state.reps} onChange={this.onChangeReps}/>
</div>
<div className="form-group">
<label>Duration: </label>
<input type="text" className="form-control" value={this.state.duration} onChange={this.onChangeDuration}/>
</div>
<div className="form-group">
<label>Date: </label>
<div><DatePicker selected={this.state.date} onChange={this.state.onChangeDate}/></div>
</div>
<div className="form-group">
<input type="submit" value="Create Workout Log" className="btn btn-primary"/>
</div>
</form>
</div>
);
}
}
I am created a dynamic form in react.js but i can not type anything value in input because onchnage function not working i don't know why i tried a lot of times but i am getting failed and adding form and deleting form is working all right only input value not working here is my code and codesandbox link https://codesandbox.io/s/reactdynamicform-02cho .
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputFields: [
{
firstName: "",
lastName: ""
}
]
};
}
handleAddFields = () => {
const values = this.state.inputFields;
values.push({ firstName: "", lastName: "" });
this.setState({
values
});
};
handleRemoveFields = index => {
const values = this.state.inputFields;
values.splice(index, 1);
this.setState({
values
});
};
async onChange(e, index) {
if (
["firstName","lastName"].includes(e.target.name)
) {
let cats = [...this.state.inputFields];
cats[index][e.target.name] = e.target.value;
await this.setState({
cats
});
}else{
this.setState({ [e.target.name]: e.target.value.toUpperCase() });
}
console.log(this.state.inputFields);
}
handleSubmit = e => {
e.preventDefault();
console.log("inputFields", this.state.inputFields);
};
render() {
return (
<>
<h1>Dynamic Form Fields in React</h1>
<form onSubmit={this.handleSubmit.bind(this)}>
<div className="form-row">
{this.state.inputFields.map((inputField, index) => (
<div key={`${inputField}~${index}`}>
<div className="form-group col-sm-6">
<label htmlFor="firstName">First Name</label>
<input
type="text"
className="form-control"
id="firstName"
name="firstName"
value={inputField.firstName}
onChange={this.onChange.bind(index)}
/>
</div>
<div className="form-group col-sm-4">
<label htmlFor="lastName">Last Name</label>
<input
type="text"
className="form-control"
id="lastName"
name="lastName"
value={inputField.lastName}
onChange={this.onChange.bind(index)}
/>
</div>
<div className="form-group col-sm-2">
<button
className="btn btn-link"
type="button"
onClick={() => this.handleRemoveFields(index)}
>
-
</button>
<button
className="btn btn-link"
type="button"
onClick={() => this.handleAddFields()}
>
+
</button>
</div>
</div>
))}
</div>
<div className="submit-button">
<button
className="btn btn-primary mr-2"
type="submit"
// onSubmit={this.handleSubmit}
>
Save
</button>
</div>
<br />
<pre>{JSON.stringify(this.state.inputFields, null, 2)}</pre>
</form>
</>
);
}
}
export default App;
You approach is not the correct. Use object to contain form values
state = {
inputFields: { firstName: '', lastName: '' }
}
onChange = (e) => {
const { name, value } = e.target;
this.setState(prevState => ({ inputFields: { ...prevState.inputFields, [name]: value } }));
}
// in jsx
<input name="firstName" onChange={this.onChange} />
try this
onChange={(e)=>{this.onChange(e, index)}}
instead of
onChange={this.onChange.bind(index)}
1) Since your inputFields state is an array, you can't just call this.state.inputFields.firstName and even less inputField.firstName.
You have to call this.state.inputsFields[0].firstName.
2) If you want the index AND the event, you have to pass the onChange event like this :
<input
type="text"
className="form-control"
id="lastName"
name="lastName"
onChange={event => this.handleChange(event, index)}
/>
handleChange = (event, index) => {
console.log(event.currentTarget.value, index);
};
// output : {whatever you type} {index of the form}
// exemple : "hello 1"
i am new to the react when i have done a crud application using React and flux i got this error my codes are below,
UserList
import React, { Component } from "react";
import $ from "jquery";
import UserStore from "../../stores/UserStore";
import * as UserActions from "../../actions/UserActions";
import AddUser from "./AddUser";
$.DataTable = require("datatables.net");
class UserList extends Component {
constructor() {
super();
this.getUsers = this.getUsers.bind(this);
this.state = {
users: UserStore.getAll()
};
this.loadUsers();
}
componentDidMount() {
$(document).ready(function() {
$("#example").DataTable({
ordering: true
});
});
}
componentWillMount() {
UserStore.on("change", this.getUsers);
}
componentWillUnmount() {
UserStore.removeListener("change", this.getUsers);
}
getUsers() {
console.log(" get users called");
this.setState({
users: UserStore.getAll()
});
}
loadUsers() {
UserActions.getUsersList();
}
render() {
const userlistitem = this.state.users.map((user, index) => (
<tr key={index}>
<th scope="row">{index}</th>
<td>{user.name}</td>
<td>{user.username}</td>
<td>{user.email}</td>
<td>{user.dob}</td>
<td>{user.address}</td>
<td>{user.mobile}</td>
<td>{user.branch}</td>
</tr>
));
return (
<div
style={{
marginTop: 80,
marginLeft: 150,
marginRight: 150
}}
>
<div className="card text-white bg-info mb-3">
<div className="card-body">
<div className="d-flex justify-content-between">
<h5>User List</h5>
<div>
<button
style={{
marginTop: 10
}}
type="button"
className="btn btn-light "
data-toggle="modal"
data-target="#exampleModalCenter"
>
Add New User
</button>
</div>
</div>
</div>
</div>
<table id="example" className="table table-bordered table-striped ">
<thead className="thead-dark">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">User Name</th>
<th scope="col">Email</th>
<th scope="col">DOB</th>
<th scope="col">Address</th>
<th scope="col">Mobile</th>
<th scope="col">Branch</th>
</tr>
</thead>
<tbody>{userlistitem}</tbody>
</table>
<AddUser />
</div>
);
}
}
export default UserList;
Add User
import React, { Component } from "react";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import "../../css/datepicker.css";
import * as UserActions from "../../actions/UserActions";
class AddUser extends Component {
constructor(props) {
super(props);
this.state = {
branch: "",
name: "",
username: "",
mobile: "",
email: "",
address: "",
dob: new Date()
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handledatepickerchange = this.handledatepickerchange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({ [name]: value });
}
handledatepickerchange(event) {
this.setState({ dob: event });
}
createUser = () => {
console.log("this is:", this);
const user = {
branch: this.state.branch,
name: this.state.name,
username: this.state.username,
mobile: this.state.mobile,
email: this.state.email,
address: this.state.address,
dob: this.state.dob
};
UserActions.createNewUser(user);
this.setState({
branch: "",
name: "",
username: "",
mobile: "",
email: "",
address: "",
dob: new Date()
});
};
render() {
return (
<div
className="modal fade"
id="exampleModalCenter"
tabIndex="-1"
role="dialog"
aria-labelledby="exampleModalCenterTitle"
aria-hidden="true"
>
<div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title" id="exampleModalCenterTitle">
Add New User
</h5>
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div className="modal-body">
<div className="input-group mb-3">
<input
name="name"
type="text"
className="form-control"
placeholder="Name"
aria-label="Name"
aria-describedby="button-addon2"
value={this.state.name}
onChange={this.handleInputChange}
/>
</div>
<div className="input-group mb-3">
<input
name="username"
type="text"
className="form-control"
placeholder="User Name"
aria-label="User Name"
aria-describedby="button-addon2"
value={this.state.username}
onChange={this.handleInputChange}
/>
</div>
<div className="input-group mb-3">
<input
name="email"
type="email"
className="form-control"
placeholder="Email"
aria-label="Email"
aria-describedby="button-addon2"
value={this.state.email}
onChange={this.handleInputChange}
/>
</div>
<div className="input-group mb-3">
<input
name="address"
type="text"
className="form-control"
placeholder="Address"
aria-label="Address"
aria-describedby="button-addon2"
value={this.state.address}
onChange={this.handleInputChange}
/>
</div>
<div className="input-group mb-3">
<input
name="branch"
type="text"
className="form-control"
placeholder="Branch"
aria-label="Branch"
aria-describedby="button-addon2"
value={this.state.branch}
onChange={this.handleInputChange}
/>
</div>
<div className="input-group mb-3">
<DatePicker
name="dob"
type="text"
className="form-control"
placeholder="DOB"
aria-label="DOB"
aria-describedby="button-addon2"
selected={this.state.dob}
onChange={this.handledatepickerchange}
/>
<p
style={{
marginTop: "5px",
fontWeight: "200",
marginLeft: "10px"
}}
>
Date of Birth(dd/mm/yyyy)
</p>
</div>
<div className="input-group mb-3">
<input
name="mobile"
type="number"
className="form-control"
placeholder="Mobile No."
aria-label="Mobile No."
aria-describedby="button-addon2"
value={this.state.mobile}
onChange={this.handleInputChange}
/>
</div>
</div>
<div className="modal-footer">
<br />
<br />
<button
type="button"
className="btn btn-secondary"
data-dismiss="modal"
>
Close
</button>
<button
type="button"
className="btn btn-primary"
data-dismiss="modal"
onClick={this.createUser}
>
Save
</button>
</div>
</div>
</div>
</div>
);
}
}
export default AddUser;
Action
import dispatcher from "../dispatcher/dispatcher";
import { BASE_URL } from "../utils/AppConstants";
export function getUsersList() {
console.log("getting the data! ");
fetch(BASE_URL + "/users")
.then(res => res.json())
.then(
result => {
console.log("res " + result);
dispatcher.dispatch({ type: "RECEIVE_USERS", users: result });
},
// Note: it's important to handle errors here instead of a catch() block so that
// we don't swallow exceptions from actual bugs in components.
error => {
// here manage error and close loading;
console.log("getting error " + error);
}
);
}
export function createNewUser(user) {
console.log("post the data!");
fetch(BASE_URL + "/saveuser", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(user)
})
.then(res => res.json())
.then(
result => {
dispatcher.dispatch({ type: "CREATE_USER", newUser: user });
},
// Note: it's important to handle errors here instead of a catch() block so that
// we don't swallow exceptions from actual bugs in components.
error => {
// here manage error and close loading;
console.log("getting error " + error);
}
);
}
Store
import { EventEmitter } from "events";
import dispatcher from "../dispatcher/dispatcher";
class UserStore extends EventEmitter {
constructor() {
super();
dispatcher.register(this.handleActions.bind(this));
this.users = [
{
branch: "19",
name: "Javcbvcsim11",
username: "zxcv",
mobile: "5645654",
email: "demo#gmail.com111",
address: "Demo vcbvcbAddress1",
dob: "2020-11-06T00:00:00.000+0000"
}
];
}
createUser(newUser) {
this.users.push(newUser);
console.log("new users lenght " + this.users.lenght);
this.emit("change");
}
getAll() {
return this.users;
}
handleActions(action) {
switch (action.type) {
case "RECEIVE_USERS": {
this.users = action.users;
this.emit("change");
break;
}
case "CREATE_USER": {
this.createUser(action.newUser);
break;
}
}
}
}
export default new UserStore();
I don't know what is the problem happened here.When checked in stack over flow i got that it's the problem of iterating the data. But i already did that. even though the error remain still.If any one can help it will be much appreciable.
The error thrown in setState method. i am here using flux as the architecture.The problem occures when i try to add a new user to my data.Actualyy when i add it inserting to db without any problem. the problem is when i click save button in my AddUser component it saves in to my db but after it need to show again as a table list but the error throws.
One problem which I can see is that you're using user.dob directly inside a JSX element (<td>{user.dob}</td>). dob is either new Date() or the direct output of react-datepicker's onChange event, both of which are objects.
Here's a dummy component which tried to render {new Date()}.
const App = ({ text }) => <p>{text}</p>;
// Passing Date object.
ReactDOM.render(<App text={new Date()} />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
This results in an error like this for me:
Objects are not valid as a React child (found: Mon Jan 21 2019 12:37:56 GMT+0530 (India Standard Time)).
Which means you're passing an object, not a string as a child to a JSX element.
Notice how this works:
const App = ({ text }) => <p>{text}</p>;
// Passing stringified Date object.
ReactDOM.render(<App text={new Date().toString()} />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I pass a string as the child of the p JSX element. You need to do something similar and make sure no objects are passed.
Actually, since you didn't post your entire error, it's hard to say which entry is causing the error. But, user.dob is my best guess.
Iterating through objects is tricky in React. Generally you want to use Object.entries/Object.values/Object.keys and then .map. This takes the object and creates an array and then you can iterate through the object with .map. .map will not work on an object because it is an array function, so you have to turn your object into an array first. The .map function will allow you to iterate in React without that error:
this.state = {
branch: "",
name: "",
username: "",
mobile: "",
email: "",
address: ""
}
// Not in your constructor
let myObj = this.state;
Object.entries(myObj).map(item => {
// Successfully iterates, do what you need to do in here
})
Also, it's a very bad idea to use JQuery with React. React uses a virtual DOM and doesn't play well with JQuery.
This question already has answers here:
ReactJS this.state null
(5 answers)
Closed 6 years ago.
I have a component in react that is a simple form, I have a function that looks if there is a change in any of the form elements and then sets the state of the form element being edited. In the handleChange function when I do console.log(this.state) I see the exact thing I expect, the correct keys eg (name, teamName etc) with the values in I have entered in the form.
However when I click submit and it calls the function nextStep I get an error message saying this.state.name is null, am I missing something here?
Here is my component.
var ReactDom = require('react-dom');
const uuid = require('uuid/v1');
import {postDataTest} from "../actions/postData";
import TeamSelectBox from "./TeamSelectBox";
import React, {Component, PropTypes} from "react";
class PlayerForm extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
teamName: '',
bio: '',
teamId: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(name, event) {
this.setState({[name]: event.target.value});
console.log(this.state);
}
nextStep(e) {
e.preventDefault();
// Get values via this.refs
var player = {
id: uuid(),
name: this.state.name,
teamName: this.state.teamName,
bio: this.state.bio,
teamId: this.state.teamId
};
postDataTest(player);
}
render() {
return (
<div className="row">
<div className="col-md-6">
<div className="panel">
<div className="panel-heading">
<h1>Add Player</h1>
</div>
<div className="panel-body">
<form className="form-horizontal">
<div className="form-group">
<label className="control-label">Name</label>
<input type="text" className="form-control" ref="name" defaultValue={this.state.name} onChange={this.handleChange.bind(this, 'name')}/>
</div>
<div className="form-group">
<label className="control-label">Team Name</label>
<input type="text" className="form-control" ref="teamName" defaultValue={this.state.teamName} onChange={this.handleChange.bind(this, 'teamName')}/>
</div>
<TeamSelectBox state={this.state.teamId} onChange={this.handleChange.bind(this, 'teamId')}/>
<div className="form-group">
<label className="control-label">Bio</label>
<input type="textarea" className="form-control" ref="bio" defaultValue={this.state.bio} onChange={this.handleChange.bind(this, 'bio')}/>
</div>
<div className="bs-component">
<button className="btn btn-md btn-default btn-block" onClick={this.nextStep}>Save & Continue</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
}
module.exports = PlayerForm;
setState doesn't mutate the state immediately and hence you must make use of callback in setState to log the updated value
handleChange(name, event) {
this.setState({[name]: event.target.value}, function() {
console.log(this.state);
});
}
Also you did not bind the nextStep function. You should do that in the constructor or any other way you prefer
class PlayerForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
teamName: '',
bio: '',
teamId: ''
};
this.handleChange = this.handleChange.bind(this);
this.nextStep = this.nextStep.bind(this);
}
handleChange(name, event) {
this.setState({[name]: event.target.value});
console.log(this.state);
}
nextStep(e) {
e.preventDefault();
// Get values via this.refs
var player = {
id: "9879",
name: this.state.name,
teamName: this.state.teamName,
bio: this.state.bio,
teamId: this.state.teamId
};
postDataTest(player);
}
render() {
return (
<div className="row">
<div className="col-md-6">
<div className="panel">
<div className="panel-heading">
<h1>Add Player</h1>
</div>
<div className="panel-body">
<form className="form-horizontal">
<div className="form-group">
<label className="control-label">Name</label>
<input type="text" className="form-control" ref="name" defaultValue={this.state.name} onChange={this.handleChange.bind(this, 'name')}/>
</div>
<div className="form-group">
<label className="control-label">Team Name</label>
<input type="text" className="form-control" ref="teamName" defaultValue={this.state.teamName} onChange={this.handleChange.bind(this, 'teamName')}/>
</div>
<div className="form-group">
<label className="control-label">Bio</label>
<input type="textarea" className="form-control" ref="bio" defaultValue={this.state.bio} onChange={this.handleChange.bind(this, 'bio')}/>
</div>
<div className="bs-component">
<button className="btn btn-md btn-default btn-block" onClick={this.nextStep}>Save & Continue</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
}
ReactDOM.render(<PlayerForm/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
<div id="app"></div>