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

react

此文转载自:https://blog.csdn.net/wh_xia_jun/article/details/109916408

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

"我们用 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);

以上是 用react 构建电子表格(2)---选择单元格上半节 的全部内容, 来源链接: utcz.com/z/383938.html

回到顶部