i'm kinda newbie on React but when I was learning about componentWillUnmount() method I found a behavior I can't understand.
Consider this script (here's the fiddle -> https://jsfiddle.net/ockhamsblade/4zf5tq2h/37/):
let myArray = [1,2,3,4,5,6];
class List extends React.Component {
constructor() {
super()
this.state = {
list: []
};
}
componentDidMount() {
this.setState({list:myArray});
}
render() {
return (
<div>
<h1>My list</h1>
<ul>
{
this.state.list.map((item, index) => (
<Item key={index} value={item} onDeleteItem={this.handleDelete}>Item {item}</Item>
))
}
</ul>
</div>
)
}
handleDelete = (value) => {
console.log("Original list: ", this.state.list);
console.log("Handling delete of item " + value);
let newState = {...this.state};
newState.list = newState.list.filter((v) => v !== value);
console.log("New list: ", newState.list);
this.setState(newState);
}
}
class Item extends React.Component {
render() {
return (<li><button type="button" onClick={this.delete}>Item {this.props.value}</button></li>);
}
delete = () => {
console.log('Please remove item ' + this.props.value);
this.props.onDeleteItem(this.props.value);
}
componentWillUnmount() {
console.log("Unmounting item " + this.props.value);
}
}
ReactDOM.render(<List />, document.querySelector("#app"))
As you could check in the JSFiddle.net script (using the developer's console), whenever you click on a item, List component removes that value from its state and re-renders the remaining items. So far so good. Thing is, before being unmounted, Item components always have the last item's props, such if these would be being overwritten or something (you can check this out in the developer's console). So, even if i'm removing the item #3 (or whichever), "this.props.value" on componentWillUnmount() is always = 6, when I would expect = 3.
I guess that, for whatever performance reason, React is removing the last item from DOM and re-rendering the rest. Is this behavior expected? In case it is, could you please refer some docs and/or tell me how to keep the original instance of the Item being removed before being unmounted? I tried to store the props.value on the item state but doesn't work either.
Thanks in advance.
The reason why you are getting this is because you are passing index of the array as key. Instead if you pass a valid unique identifier this will not occur.
Pleas run this code you will understand.
let myArray = [1,2,3,4,5,6];
class List extends React.Component {
constructor() {
super()
this.state = {
list: []
};
}
componentDidMount() {
this.setState({list:myArray});
}
render() {
return (
<div>
<h1>My list</h1>
<ul>
{
this.state.list.map((item, index) => (
<Item key={`${item}`} value={item} onDeleteItem={this.handleDelete}>Item {item}</Item>
))
}
</ul>
</div>
)
}
handleDelete = (value) => {
console.log("Original list: ", this.state.list);
console.log("Handling delete of item " + value);
let newState = {...this.state};
newState.list = newState.list.filter((v) => v !== value);
console.log("New list: ", newState.list);
this.setState(newState);
}
}
class Item extends React.Component {
render() {
return (<li><button type="button" onClick={this.delete}>Item {this.props.value}</button></li>);
}
delete = () => {
console.log('Please remove item ' + this.props.value);
this.props.onDeleteItem(this.props.value);
}
componentDidUpdate(){
console.log("incdu",this.props.value);
}
componentWillUnmount() {
console.log("Unmounting item " + this.props.value);
}
}
ReactDOM.render(<List />, document.querySelector("#app"))
The only change I have made here is passing a proper identifier (here the item itself) as key.
<Item key={`${item}`} value={item} onDeleteItem={this.handleDelete}>Item {item}</Item>
You can refer Understanding unique keys for array children in React.js