Reputation: 83
I know this is a simple problem but I am new with React.
What am I missing to get the index of a value that I click on? I am simply trying to say, when a user clicks delete, delete that value from the array.
<!DOCTYPE html>
<html>
<head>
<title>
React Practice
</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.js"></script>
</head>
<body>
<div id="app">
<!-- This element's contents will be replaced with your component. -->
</div>
<script type="text/babel">
var MainContainer = React.createClass({
getInitialState: function(){
return {
name: 'JK_MNO',
friends: [],
text: ''
}
},
handleChange: function(e){
this.setState({
text: e.target.value
});
},
handleSubmit: function(e){
e.preventDefault();
if(this.state.text !== '') {
var nextfriend = this.state.friends.concat([{
text: this.state.text, id: Date.now()
}]);
var nextText = '';
this.setState({
friends: nextfriend, text: nextText
});
}
},
handleDelete: function(e){
for (var i = this.state.friends.length - 1; i >= 0; i--) {
this.state.friends[i]
};
this.state.friends.splice(i, 1);
this.setState({
friends: this.state.friends
});
},
render: function(){
return (
<div>
<h3> Name: {this.state.name} </h3>
<ShowList friends={this.state.friends} handleDelete={this.handleDelete} />
<form onSubmit={this.handleSubmit} >
Enter Friends: <input className="friendInput" onChange={this.handleChange} value={this.state.text} />
</form>
</div>
);
}
});
var ShowList = React.createClass({
render: function() {
var createFriend = function(friend) {
return (
<li key={friend.id}>{friend.text} <button onClick={this.props.handleDelete}>Delete</button> </li>
);
};
return <ul>{this.props.friends.map(createFriend.bind(this))}</ul>;
}
});
ReactDOM.render(<MainContainer />, document.getElementById('app'));
</script>
Upvotes: 2
Views: 4439
Reputation: 83
After struggling with getting some of the previous answers to compile I came up with this simple way.
index
comes from the closure function where I'm mapping the array. I use a copy of the array to avoid changing the current state of it.
This works.
<!DOCTYPE html>
<html>
<head>
<title>
React Practice: Friends List
</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.js"></script>
</head>
<body>
<div id="app">
<!-- This element's contents will be replaced with your component. -->
</div>
<script type="text/babel">
var MainContainer = React.createClass({
getInitialState: function(){
return {
name: 'JK_MNO',
friends: [],
text: ''
}
},
handleChange: function(e){
this.setState({
text: e.target.value
});
},
handleSubmit: function(e){
e.preventDefault();
if(this.state.text !== '') {
var nextfriend = this.state.friends.concat([{
text: this.state.text, id: Date.now()
}]);
var nextText = '';
this.setState({
friends: nextfriend, text: nextText
});
}
},
handleDelete: function(e){
var i = e.target.value;
var friendsCopy = this.state.friends;
this.setState(state => {
friendsCopy.splice(i, 1);
friends: friendsCopy
});
},
render: function(){
return (
<div>
<h3> Name: {this.state.name} </h3>
<ShowList friends={this.state.friends} handleDelete={this.handleDelete} />
<form onSubmit={this.handleSubmit} >
Enter Friends: <input className="friendInput" onChange={this.handleChange} value={this.state.text} />
</form>
</div>
);
}
});
var ShowList = React.createClass({
render: function() {
var createFriend = function(friend, index) {
return (
<li key={friend.id}>{friend.text} <button onClick={this.props.handleDelete} value={index}>Delete</button> </li>
);
};
return <ul>{this.props.friends.map(createFriend.bind(this))}</ul>;
}
});
ReactDOM.render(<MainContainer />, document.getElementById('app'));
</script>
</body>
</html>
Upvotes: 0
Reputation: 6266
Using map you may generate your components , afterwards using some ES6 syntactic sugar you can pass each element's index to your click handler as show below:
handleDelete: function(e, i) {
e.preventDefault();
// Array manipulation
},
render: function() {
return (
<div>
<ul>
{this.state.users.map((user,i)=> <li onClick={(e) => this.handleDelete(e,i)}>{user}</li>)}
</ul>
</div>
)
}
Upvotes: 0
Reputation: 66
When mapping over an array, the index of the current item under consideration in the array is the second argument applied to the closure (function) provided to map.
Example:
const arr = ['a', 'b', 'c', 'd'];
arr.map((item, index, arr) => {
console.log (item, index, arr);
});
//'a' 0 ['a', 'b', 'c', 'd']
//'b' 1 ['a', ...]
//'c' 2 [...]
//'d' 3 [...]
Also, the full array being mapped over is the third arg.
You can add an 'data-index' property to the element and then reference it in the click event.
<li 'data-index'={index}>... <li>
handleDelete: function (e) {
const index = e.currentTarget.dataset.index;
//do what you need to delete
}
Using your code:
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
...
<script type="text/babel">
var MainContainer = React.createClass({
getInitialState: ...,
handleChange: ...,
handleSubmit: ...,
//Here's where you are trying to reference index of clicked element
handleDelete: function (e) {
//The event object 'e' contains a reference to the clicked
//element at property 'currentTarget'.
const clickedEl = e.currentTarget;
//Setting the 'data-[varname]' property when you create the
//element in the render method is a safe way to set custom
//properties you would like to access from a DOM element. Those props
//are then accessible via the 'dataset' property from the DOM element.
//Since we set 'data-index' below, we can find the index of the
//clicked element with clickedEl.dataset.index.
const clickedIndex = clickedEl.dataset.index;
//When using React, never act directly on properties of component
//state. Your previous code:
// this.state.friends.splice(i, 1);
//did that. Instead, make a copy of your state, make changes to
//that copy, and update state with that copy.
//Best practice is to try to keep your state as simple as possible,
//so consider refactoring to simplify your friends state so that it is
//not an array of objects. But, with current implementation
//one way might be something like this.
const friendsCopy = this.state.friends.map(friend => {
return {
id: friend.id,
text: friend.text,
...other properties of a friend
};
});
//You can splice out the clicked friend
//(or remove some other way)
friendsCopy.splice(clickedIndex, 1);
//Set state with copy
this.setState({
friends: friendsCopy
});
},
render: function(){
return (
<div>
...
</div>
);
}
});
var ShowList = React.createClass({
render: function() {
//Create friend is the closure you are providing
//to map function for friends. Set the second arg
//to index to reference the index of the friend being
//considered. You can then safely set a property of 'data-[varname]'
//on the element to reference index later. By preceding the
//var name with 'data-', you'll be able to reference the property
//later via a clean 'dataset' object without risking overwriting some
//important property on the DOM object called 'varname'. Here I'll use
//the varname 'index'.
var createFriend = function(friend, index) {
return (
<li key={friend.id}>{friend.text} <button 'data-index'={index} onClick={this.props.handleDelete}>Delete</button> </li>
);
};
return <ul>{this.props.friends.map(createFriend.bind(this))}</ul>;
}
});
ReactDOM.render(<MainContainer />, document.getElementById('app'));
</script>
Hope that clears things up a bit. It looks like you're trying to make changes directly to this.state in a few places on components. Avoid doing that, instead get in the habit of copying state->altering copy->updating state. Try reading back through the React docs as well, they're quite good.
Upvotes: 3
Reputation: 1964
When you generate each button you could give the ID of that button the corresponding index that the element is in the array. Then your handleDelete function will have to check the clicked elements ID value to remove that element from the array.
This question actually might be a duplicate of how to remove an item from a list with a click event in ReactJS?
Upvotes: 0