Reputation: 359
I'm learning React and I'm trying to do a little project. This project is showing a list of users of a jsonPlaceholder
and wanted to make it to edit the fields.
I have a div that loads the list, another hidden div with editable fields and a button that when clicked the list div is hidden and the other one appears.
When I click the button it gives the following error:
"TypeError: Can not read property 'handleClick' of undefined"
Can anyone help?
import React from 'react';
import './App.css';
class User extends React.Component {
constructor(){
super();
this.state = {
users: [],
displayList: 'block',
displayForm: 'none'
}
this.handleClick = this.handleClick.bind(this);
}
componentDidMount(){
this.getUsers();
}
getUsers(){
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(json => this.setState({users: json}));
}
handleClick(){
this.setState({displayList: 'none', displayForm: 'block'})
}
render(){
const list = this.state.displayList;
const form = this.state.displayForm;
return (
<ul>
{this.state.users.map(function(item){
return (
<div key={item.id}>
<br></br>
<div style={{display: list}}>
<h2>{item.name} </h2>
<p>Email: {item.email} </p>
<p>Address: </p>
<ul>
<li>Street: {item.address.street} </li>
<li>Suite: {item.address.suite} </li>
<li>City: {item.address.city} </li>
<li>ZipCode: {item.address.zipcode} </li>
</ul>
<p>Phone: {item.phone} </ p>
</div>
<div style={{display: form}} >
<div>
<h2>{item.name} </h2>
<p>Email: <input value = {item.email} className="form-control" onChange=""/> </p>
<p>Address: </p>
<ul>
<li>Street: <input value = {item.address.street} className="form-control" /> </li>
<li>Suite: <input value = {item.address.suite} className="form-control" /> </li>
<li>City: <input value = {item.address.city} className="form-control" /> </li>
<li>ZipCode: <input value = {item.address.zipcode} className="form-control" /> </li>
</ul>
<p>Phone: <input value = {item.phone} className="form-control" /> </p>
</div>
</div>
<button onClick={() => this.handleClick()}> Edit </button>
<hr></hr>
</div>
)
})}
</ul>
);
}
}
export default User;
Upvotes: 0
Views: 1332
Reputation: 7665
The issue is that this.handleClick
is referenced inside a callback defined as a regular function
that is passed to a map
. Regular function have their own values of this
which is not the component.
In order to retain the value of this you call pass the callback to the map as an arrow function:
render () {
/* ... */
return (
<ul>
{this.state.users.map((item) => { /* ... */ })}
</ul>
)
}
Alternatively you can explicitly pass this
value as a second argument to the map
render () {
/* ... */
return (
<ul>
{this.state.users.map(function(item) { /* ... */ }, this)}
</ul>
)
}
Upvotes: 0
Reputation: 920
You have to bind the method to the object.
Either:
handleClick = () => {
this.setState({displayList: 'none', displayForm: 'block'})
}
or:
<button onClick={this.handleClick.bind(this)}> Edit </button>
Otherwise it loses the context.
Upvotes: 0
Reputation: 3103
It is because the callback in the map method this.state.users.map
is not bound. You need to turn that into an arrow function. like this:
import React from 'react';
import './App.css';
class User extends React.Component {
constructor(){
super();
this.state = {
users: [],
displayList: 'block',
displayForm: 'none'
}
this.handleClick = this.handleClick.bind(this);
}
componentDidMount(){
this.getUsers();
}
getUsers(id){
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(json => this.setState({users: json}));
}
handleClick(){
this.setState({displayList: 'none', displayForm: 'block'})
}
render(){
const list = this.state.displayList;
const form = this.state.displayForm;
return (
<ul>
{this.state.users.map((item => {
return (
<div key={item.id}>
<br></br>
<div style={{display: list}}>
<h2>{item.name} </h2>
<p>Email: {item.email} </p>
<p>Address: </p>
<ul>
<li>Street: {item.address.street} </li>
<li>Suite: {item.address.suite} </li>
<li>City: {item.address.city} </li>
<li>ZipCode: {item.address.zipcode} </li>
</ul>
<p>Phone: {item.phone} </ p>
</div>
<div style={{display: form}} >
<div>
<h2>{item.name} </h2>
<p>Email: <input value = {item.email} className="form-control" onChange=""/> </p>
<p>Address: </p>
<ul>
<li>Street: <input value = {item.address.street} className="form-control" /> </li>
<li>Suite: <input value = {item.address.suite} className="form-control" /> </li>
<li>City: <input value = {item.address.city} className="form-control" /> </li>
<li>ZipCode: <input value = {item.address.zipcode} className="form-control" /> </li>
</ul>
<p>Phone: <input value = {item.phone} className="form-control" /> </p>
</div>
</div>
<button onClick={this.handleClick}> Edit </button>
<hr></hr>
</div>
)
}))}
</ul>
);
}
}
export default User;
Upvotes: 2