用react 构建电子表格(2)---选择单元格上半节

表格,首先要解决选择单元格的问题,选择相应单元格后,才能进一步修改样式等。

"我们用 state 来实现所谓“记忆”的功能"。

点击单元格的时候,必须给它加上某种记忆,点击放在table上,还是放在单元格上呢?这是个问题。

ps:样式也是一种数据,必须把相关类加上。

选择单元格,有2个要素:

  1. 记忆选择的单元格
  2. 表现选择的单元格

单元格的选择,当然靠点击事件,或者鼠标点击+按键盘特定键来解决。

react的点击事件,目前我理解就是js点击事件,语法略有差异而已。React 事件处理

记忆点击的单元格,涉及数据的存储。一个是存在对象本身,一个是向上级组件传播。

当前,每个 cell 组件都维护了选择的状态。我们可以把所有 cell 的是否被选择放在一个地方。

你可能会想,我们也可以在table 组件中收集每个格子组件 中的 state。虽然技术上来讲是可以实现的,但是代码如此编写会让人很难理解,并且我们以后想要维护重构时也会非常困难。所以,最好的解决方式是直接将所有的 state 状态数据存储在 table父组件当中。之后 table 组件可以将这些数据通过 props 传递给各个 cell子组件。

先在table 当中搞个数组,用于记忆每个是否选中状态。

这里是搞个对象封装它们呢,还是直接原生数据类型呢 纠结,先搞个原生数据类型数组。需要的时候再扩展为对象,把是否被选中作为这个对象的属性。

    constructor(props) {
        super(props);
        let r = this.props.rowNum;  //行数
        let c = this.props.colNum;  //列数
        let cellSelectStates=[];    //单元格是否被选中
        for(let i=0;i<r;i++){
            cellSelectStates[i]=new Array();
            for(let j=0;j<c;j++){
                cellSelectStates[i][j]=false;
            }
        }

        this.state = {
            cellSelectStates:cellSelectStates ,//单元格是否被选中
        };
    }

然后逐级向下传递:

    getRows(colNum, rowNo,rowIndex) {
        return <CommRow colNum={colNum}
                        rowNo={rowNo}
                        key={"dataRow" + rowNo + "_" + colNum}
                        isSelect_cells={this.state.cellSelectStates[rowIndex]}
        />
    }
export default class CommRow extends Component {

    constructor(props) {
        super(props);
        this.state = {
            //一维数组  标记一行内单元格选中情况 由prop传入
            cellSelectStates_row:this.props.isSelect_cells ,//单元格是否被选中,
        };
    }
.....
}

行向单元格传递以此类推。

点击事件包括:单元格点击、行点击、表点击、页面点击

选择单元格,我们当然首先考虑单元格的点击。

表格编辑一般会有区域选中的问题,我们先只考虑选中一个单元格的情况。

先在单元格上加点击事件。

接下来,我们要修改一下 Cell的点击事件监听函数。Table 组件当前记忆了单元格选择情况。我们需要想办法让Cell 去更新 Table 的 state。由于 state 对于每个组件来说是私有的,因此我们不能直接通过 Cell来更新 Table 的 state。

相反,从 Table 组件向 Row 组件传递一个函数,Row组件向Cell组件传递一个函数,当Cell 被点击的时候,这个函数就会被调用。接着,我们将 Row组件的getCommCell 方法改写为如下效果:

    getCommCell(rowNo,colNo,isSelected) {
        return <CommCell
            rowNo={rowNo}
            colNo={colNo}
            key={"CommCell"+rowNo+"_"+colNo}
            isSelected={isSelected}
            onClick={() => this.handleClick(isSelected)}
        />
    }

现在我们从 Row组件向 Cell 组件中传递props 参数中:有 onClickonClick prop 是一个 Cell 组件点击事件监听函数。接下来,我们需要修改 Cell的代码:

进行上述修改之后,代码会变成下面这样:

    render() {
       return (
            <td  className="ttd"
                 onClick={() => this.props.onClick()}
            >
     </td>
        )
    }

一个 cell被点击时,row 提供的 onClick 函数就会触发。我们回顾一下这是怎么实现的:

  1. 向 DOM 内置元素 <td> /cell添加 onClick prop,让 React 开启对点击事件的监听。
  2. 当 td被点击时,React 会调用 cell组件的 render() 方法中的 onClick 事件处理函数。
  3. 事件处理函数触发了传入其中的 this.props.onClick() 方法。这个方法是由 row传递给cell 的。
  4. 由于 row把 onClick={() => this.handleClick(i)} 传递给了 cell,所以当 cell中的事件处理函数触发时,其实就是触发的 row当中的 this.handleClick(i) 方法。
  5. 现在我们还尚未定义 handleClick() 方法,所以代码还不能正常工作。如果此时点击 td,你会在屏幕上看到红色的错误提示,提示内容为:“this.handleClick is not a function”

这时候我们点击 td的时候,浏览器会报错,因为我们还没有给row定义 handleClick 方法。我们现在来向 row 里添加 handleClick 方法:

export default class CommRow extends Component {

    constructor(props) {
        super(props);
        this.state = {
            //一维数组  标记一行内单元格选中情况 由prop传入
            cellSelectStates_row:this.props.isSelect_cells ,//单元格是否被选中,
        };
    }
    handleClick(selectStatus) {
        alert('row')
    }
....
}

 这里,我们应该进一步调用table对象的方法,table当中有个函数,专门处理选择单元格的事情:

//处理单元格选择
    handleCellSelect = (i,j) => {
          alert(i);
        //复制一个数组
        let cellSelectStates_s=this.state.cellSelectStates;
        let cellSelectStates_temp=[];
        for(let i=0;i<cellSelectStates_s.length;i++){
            let [...cellSelectStates_r]=cellSelectStates_s[i];
            cellSelectStates_temp.push(cellSelectStates_r);
        }
       // cellSelectStates_temp[0][0]=true;
        //this.setState({cellSelectStates: cellSelectStates_temp});

    }

这个方法,需要通过prop向row对象传递。

    getRows(colNum, rowNo,rowIndex) {
        return <CommRow colNum={colNum}
                        rowNo={rowNo}
                        key={"dataRow" + rowNo + "_" + colNum}
                        isSelect_cells={this.state.cellSelectStates[rowIndex]}
                        handleCellSelect={(i,j)=> this.handleCellSelect(i,j)}
        />
    }

row对象当中通过prop 获取这个函数

    handleClick=(rowNo,colNo)=> {
        alert('row'+rowNo+"_"+colNo);
         this.props.handleCellSelect(rowNo,colNo);

    }

 row获取到父对象函数后,接力想cell传递函数:

    getCommCell(rowNo,colNo,isSelected) {
        return <CommCell
            rowNo={rowNo}
            colNo={colNo}
            key={"CommCell"+rowNo+"_"+colNo}
            isSelected={isSelected}
            onClick={() => this.handleClick(rowNo,colNo)}
        />
    }

cell 当中直接使用:

//Cell.js
import React, { Component } from 'react';

export default class CommCell extends Component {

    constructor(props) {
        super(props);
        this.state = {
            // 标记单元格选中情况 由prop传入
            isSelected:this.props.isSelected ,//单元格是否被选中,
        };
    }

    render() {
       return (
            <td  className="ttd"
                 onClick={() => this.props.onClick()}
            >
     </td>
        )
    }
}

一个函数,从Table出发,传到到row,由row继续传递到cell.

点击单元格后,怎么表现呢 目前我觉得有2种方式:

1、搞个边框线,把选择的单元格框起来,这样做的问题好处是 对表格没啥侵入性,缺陷是略复杂,因为要获取选择的单元个的位置信息(坐标信息),再搞线的定位。

2、修改选择单元格的样式 如修改相关单元格的边线样式、或者修改选择单元格的背景色

修改单元格边线样式?其实要找出选择区域的边界上个各个单元格。

修改选中单元格的背景色,似乎更简单,通过色块即可标记处选择区域。

下节即介绍选择单元格显示的实现。

ps:二维数组copy,不解释,看代码

二维数组copy   注意不能用slice
var a = [[1,2,3],[2,3,4]];
console.log(a);
console.log("========================");
var b=a.slice();

let re=[];
for(let i=0;i<a.length;i++){
    console.log("i"+i);
    let [...arr1]=a[i];
    console.log(arr1);
    re.push(arr1);
}
console.log("--------------------");
console.log(re);


re[1][1]=re[1][1]*re[1][1];

console.log("**************************************");
console.log(re);
console.log("#################################");
console.log(a);

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值