componentWillUnmount() 打印出来组件自身的数据不太对,请大神们指点

#1
import React,{Component} from 'react' ;

let initList = [
    {name:'zhang-3',addr:'henan'},
    {name:'li-4',addr:'beijing'},
    {name:'wang-4',addr:'beijing'},
    {name:'zhao-6',addr:'tianjin'},
] ;


let liStyleObj = {
    cursor:'pointer',
    height:'25px',
    borderBottom:'1px solid black',
    listStyle:'none'
} ;

class LiComp extends Component {
    componentWillUnmount() {
        console.info('--------- componentWillUnmount start ------------') ;  
        const { record, index } = this.props;
        console.info('index : ' ,index) ;
        console.info('record : ' , JSON.stringify(record,null,2)) ;
        console.info('--------- componentWillUnmount end ------------') ;  
    }
    handleDeleteItemClick = e => {
        console.info('--------- handleDeleteItemClick start ------------') ;  
        let {record,index,removeItemFn} = this.props ;    
        console.info('index : ' ,index) ;    
        console.info('record : ' , JSON.stringify(record,null,2)) ;
        removeItemFn(record) ;
        console.info('--------- handleDeleteItemClick end ------------') ;  
    }
    render (){
        let {index,record} = this.props ;
        return (
            <li style={liStyleObj} onClick ={this.handleDeleteItemClick}>name:{record.name} , addr : {record.addr}  --- 点击删除</li>
        ) ;
    }
}

class Demo03 extends Component{
    constructor(props){
        super(props) ;
        this.state = {
          list:initList
        } ;
    }
    removeItemFn = (record) => {
       let newList = this.state.list.filter(item=> item !== record ) ;
       this.setState({list:newList}) ;
    }
    showData = ()=>{
        console.info(JSON.stringify(this.state.list,null,2) ) ;
    }
    renderAllLi(){
       return this.state.list.map((record,index)=>{
           return (<LiComp key ={index} index ={index} record ={record} 
                    removeItemFn = {this.removeItemFn}  />) ;
       }) ;
    }
    render () {
        return (
            <div>
                <ul>
                    {this.renderAllLi()}
                </ul>
                <button className ="btn" onClick ={this.showData}>test</button>
            </div>
        ) ;
    }
}
export default Demo03 ;

列表项显示

当点击第一条记录后列表项1正常被删除

,但是console打印出来的数据有问题

也就是说 li组件 的componentWillUnmount函数中获取的数据有问题,请大神们指点多谢

代码截图

#2

在回答之前,问一个问题,删除操作删的是什么?自己想想

删的是数据对吧, 是父组件的this.state.list中的一条记录。这个时候dom还没有任何的变化。

我们知道组件的state发生改变是要引发组件一系列生命周期钩子的,有下面这些:

"shouldComponentUpdate"
"componentWillUpdate"
"render"
"componentDidUpdate"

这时,父组件(Demo03)重新渲染了,子组件(liComp)也要更新吧。于是子组件就开始更新了。

因为删除的是第一项({name:‘zhang-3’,addr:‘henan’} ),于是乎:

用{name:‘li-4’,addr:‘beijing’} 替换 {name:‘zhang-3’,addr:‘henan’} (操作删除的一项)

用{name:‘wang-4’,addr:‘beijing’} 替换 {name:‘li-4’,addr:‘beijing’}

用{name:‘zhao-6’,addr:‘tianjin’} 替换 {name:‘wang-4’,addr:‘beijing’}

最后发现原来的{name:‘zhao-6’,addr:‘tianjin’} 多余,就卸载掉

#3

非常感谢你的耐心回答啊, 你说的这个,我之前有考虑过。 我在rc-table源码中加了一行日志,。然后测试同样的删除list列表中的一项,正常删除列表项,但是如rc-table的console是对的,不知道问什么有点奇怪。

这个是rc-table的源码地址 : http://react-component.github.io/table/
下面是我写的测试代码
import React,{Component} from ‘react’ ;
import Table from ‘…/components/rctable/Table.jsx’ ;

class CheckBox extends Component{
render() {
const props = this.props;
return (


{props.id}

);
}
}

class MyTable extends Component{

constructor(props){
super(props) ;
this.state = {
data: props.data,
} ;
}

remove(index) {
const rows = this.state.data;
rows.splice(index, 1);
this.setState({
data: rows,
});
}

handleClick(index) {
const self = this;
return () => {
self.remove(index);
};
}

checkbox(a) {
return ;
}

renderAction = (o, row, index) => {
return Delete;
}

render() {
const state = this.state;
const columns = [
{ title: ‘title1’, dataIndex: ‘a’, key: ‘a’, width: 100, render: this.checkbox },
{ title: ‘title2’, dataIndex: ‘b’, key: ‘b’, width: 100 },
{ title: ‘title3’, dataIndex: ‘c’, key: ‘c’, width: 200 },
{ title: ‘Operations’, dataIndex: ‘’, key: ‘x’, render: this.renderAction },
];
return (
<Table columns={columns} data={state.data} className=“table” rowKey={record => record.a} />
);
}
}

const data = [{ a: ‘123’ }, { a: ‘cdd’, b: ‘edd’ }, { a: ‘1333’, c: ‘eee’, d: 2 }];

class RcTable001 extends Component{
render() {
return (


specify key




);
}

}

export default RcTable001 ;

代码截图


#4

这就涉及到react diff算法的问题。
###层级比较

对于不同节点类型,React才用层级比较。这个时facebook大牛做的一个优化,让diff的复杂度为O(n),用传统方法则是O(n^3)。

React只会对相同颜色方框内的DOM节点进行比较,即同一个父节点下的所有子节点。当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个DOM树的比较。如果有,则添加。

会有下面的一系列更新
C will unmount.
C is created.
B is updated.
A is updated.
C did mount.
D is updated.
R is updated.

###列表节点的diff

  1. 当你的list指定了key属性.

react利用key来识别组件,它是一种身份标识标识,就像我们的身份证用来辨识一个人一样。每个key对应一个组件,相同的key react认为是同一个组件。

  • key相同,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新。
  • key值不同,则react先销毁该组件(有状态组件的componentWillUnmount会执行),然后重新创建该组件(有状态组件的
    为什么这么做呢?当对列表进行一系列操作(增删改查)的时候,dom结构的变化更少,根据key来。
  1. 当没有提供key,则会按照层级比较的方式更新。

    在层级比较的情况下,会有如下更新
    B will unmount.
    C will unmount.
    C is created.
    B is created.
    C did mount.
    B did mount.
    A is updated.
    R is updated.

而指定了key的比较则是如下更新
C is updated.
B is updated.
A is updated.
R is updated.

###总结

  1. 对于自己的写法,因为key指定的时index, 所以当删除{name:‘zhang-3’,addr:‘henan’}时,重新渲染,第一个key = index还是0,没有变化,所以只会更新组件。

用{name:‘li-4’,addr:‘beijing’} 替换 {name:‘zhang-3’,addr:‘henan’} (操作删除的一项)

用{name:‘wang-4’,addr:‘beijing’} 替换 {name:‘li-4’,addr:‘beijing’}

用{name:‘zhao-6’,addr:‘tianjin’} 替换 {name:‘wang-4’,addr:‘beijing’}

最后发现原来的{name:‘zhao-6’,addr:‘tianjin’} 多余,就卸载掉

  1. 然而, 用rc-table,你表格行的key是racord.a(rowKey指定的)。 当你删除第一项时。React发现key变化了,所以会直接卸载掉。

###总结为一句话
写React组件时,写key比不写key要好,key是自己数据的唯一标识,比key为index要好。

#5

:+1:
非常非常感谢啊

看了你详细的指点,收获挺多。我等下测试下。

今天公司组织出去玩,刚回来所以才回复你。

刚刚测试过完全是你说的那样,我把rc-table的key用index的话,效果和我之前的那个demo一样。
再次感谢啊