【问题标题】:Select/deselect rows in custom table in React在 React 中选择/取消选择自定义表中的行
【发布时间】:2019-01-31 10:03:48
【问题描述】:

我有一个表格组件,每行和标题都有复选框。除此之外,每一行都有从对象数组dataSet 呈现的名字、姓氏和年龄。我想要一种能够通过单击标题中的复选框并捕获所有数据来选择/取消选择所有行的方法。

同时,我还希望能够选择/取消选择单个行。同样,我想捕获所选每一行的数据。

这是组件:

class Table extends React.Component {
    render() {
        return( 
            <table style={{width:"100%"}}>
              <tr>
                <th><input type="checkbox" name="name1" /></th>
                <th>Firstname</th>
                <th>Lastname</th> 
                <th>Age</th>
              </tr>
                <tbody>{this.renderRow()}</tbody>
            </table>
        );
    }

    renderRow = () => {
        const dataSet = [
            {
                FirstName: "Bob",
                LastName: "Ross",
                Age: 50
            },
            {
                FirstName: "John",
                LastName: "Doe",
                Age: 20
            },
            {
                FirstName: "Jane",
                LastName: "Doe",
                Age: 30
            }
        ];

        return dataSet.map((item) => {
            return (
                <tr>
                    <td><input type="checkbox" name="name1" /></td>
                    <td>{item.FirstName}</td>
                    <td>{item.LastName}</td>
                    <td>{item.Age}</td>
                </tr>
            )
        })
    }
};

这是该组件的运行版本:https://codepen.io/anon/pen/exgeBg

【问题讨论】:

  • 为什么dataSet在renderRow中声明?
  • @Vencovsky 没有真正的原因,它也可以在外面。
  • 答案中是否缺少某些内容?

标签: javascript reactjs


【解决方案1】:

首先,您需要为每个项目指定某种id,以便在映射它们时将它们彼此区分开来并指定key

const dataSet = [
    {
        id: 45612,
        FirstName: "Bob",
        LastName: "Ross",
        Age: 50
    },
    {
        id: 6542,
        FirstName: "John",
        LastName: "Doe",
        Age: 20
    },
    {
        id: 7837,
        FirstName: "Jane",
        LastName: "Doe",
        Age: 30
    }
];

添加密钥:

return (
    <tr key={item.id}>

要知道选择了哪个项目,您需要在复选框的onClick 功能中发送项目本身:

<td><input type="checkbox" name="name1" onClick={this.rowSelected(item)}/></td>

你的类中的接收函数会声明如下,可以接收item,然后点击事件:

rowSelected = rowData => ev => {

现在棘手的部分是将状态设置为包含每个选定的行。如果复选框被选中,我们会将项目添加到先前选择的项目中,如果没有,我们将从数组中过滤掉该项目:

rowSelected = rowData => ev => {
    this.setState(prev => ({ 
        selection:  ev.target.checked ? [...prev.selection, rowData] : prev.selection.filter(item => item.id !== rowData.id)
    }))
}

您现在可以访问this.state.selection中的选定项目

完整代码:

class Table extends React.Component {

    rowSelected = rowData => ev => {
        this.setState(prev => ({ 
            selection:  ev.target.checked ? [...prev.selection, rowData] : prev.selection.filter(item => item.id !== rowData.id)
        }))
    }

    render() {
        return (
            <table style={{ width: "100%" }}>
                <tr>
                    <th><input type="checkbox" name="name1" /></th>
                    <th>Firstname</th>
                    <th>Lastname</th>
                    <th>Age</th>
                </tr>
                <tbody>{this.renderRow()}</tbody>
            </table>
        );
    }

    renderRow = () => {
        const dataSet = [
            {
                id: 45612,
                FirstName: "Bob",
                LastName: "Ross",
                Age: 50
            },
            {
                id: 6542,
                FirstName: "John",
                LastName: "Doe",
                Age: 20
            },
            {
                id: 7837,
                FirstName: "Jane",
                LastName: "Doe",
                Age: 30
            }
        ];

        return dataSet.map((item) => {
            return (
                <tr key={item.id}>
                    <td><input type="checkbox" name="name1" onClick={this.rowSelected(item)}/></td>
                    <td>{item.FirstName}</td>
                    <td>{item.LastName}</td>
                    <td>{item.Age}</td>
                </tr>
            )
        })
    }
};

编辑

要制作“全选”按钮,您必须制作另一个功能:

selectAll = ev => {
    this.setState(prev => ({ selction: prev.selection.length ? [] : dataSet }))
}

之前的选择是不是已经有元素了,会被清空,如果什么都没有,会被你的整个数据集替换;您需要将其存储在其他地方,例如课堂外或this.dataset

如果您希望自动勾选您的复选框,您需要设置他们的 checked 道具(IIRC),如果相应的项目在您的状态中的某个位置,则需要通过验证:

<input type="checkbox" name="name1" onClick={this.rowSelected(item)} checked={this.state.selection.some(slc => slc.id === item.id)}/>

【讨论】:

  • 在你提供的代码的当前版本中,你没有他要求I want a way to be able to select/deselect all rows by clicking on the checkbox in the header的地方
  • @Vencovsky 是对的......这给了我数据,但没有办法选择/取消选择所有行。
  • 感谢您的回答,我在正确的轨道上。我正在使用一个自定义 Checkbox 组件,该组件具有一个 Value 属性,该属性返回单击/未单击的布尔值。我通过将其存储在状态中并将其设置为设置Value={this.state.IsSelected} 来更新它。问题是,我如何使IsSelected 仅适用于所选项目。现在它选择/取消选择所有。
  • 如果没有一点代码,很难找出问题所在
【解决方案2】:

发生了什么变化:

首先你需要保持复选框的状态,你可以在这部分代码中设置一个默认值,以防你想要一些复选框开始被选中:

state = {
    header: false,
    fields: [false, false, false, false]
}

现在您需要处理程序,当某些复选框发生变化时。
有很多方法可以做到这一点,但为了简化我使用了两个函数 changeCheckBoxFields 接收要更改的复选框的索引,changeAllCheckBox 更改标题复选框的值,所有其他复选框将具有与他相同的值

CodePen中运行的完整代码

完整代码:

class Table extends React.Component {
    state = {
        header: false,
        fields: [],
        dataSet: []
    }

    changeAllCheckBox = () => {
            let {
                header,
                fields,
        } = this.state
        let newHeader = !header
        let newFields = fields.map((f) => {
            return {
                checked: newHeader,
                data: f.data
            }   
        })
        this.setState({
            fields: newFields,
            header: newHeader
        })
    }

    changeCheckBoxFields = (item, index) => {
        let newState = this.state.fields
        newState[index] = {
              check: !newState[index],
              data: item
        }
        this.setState({fields: newState})
    }

    componentDidMount() {
        const dataSet = [
            {
                FirstName: "Bob",
                LastName: "Ross",
                Age: 50
            },
            {
                FirstName: "John",
                LastName: "Doe",
                Age: 20
            },
            {
                FirstName: "Jane",
                LastName: "Doe",
                Age: 30
            }
        ];
      let fields = dataSet.map((d, i) => {return {checked: false} } )
      this.setState({
            dataSet: dataSet,
            fields: fields,
      })
    }

    render() {
        return( 
            <table style={{width:"100%"}}>
              <tr>
                <th><input type="checkbox" name="name1" onChange={this.changeAllCheckBox} /></th>
                <th>Firstname</th>
                <th>Lastname</th> 
                <th>Age</th>
              </tr>
                <tbody>{this.renderRow()}</tbody>
            </table>
        );
    }

    renderRow = () => {

        return this.state.dataSet.map((item, index) => {                
            return (
                <tr key={index}>
                    <td>
                        <input type="checkbox" name="name1" onChange={() => this.changeCheckBoxFields(item, index)}  checked={this.state.fields[index].checked} />
                    </td>
                    <td>{item.FirstName}</td>
                    <td>{item.LastName}</td>
                    <td>{item.Age}</td>
                </tr>
            )
        })
    }
};

有关反应检查中输入的更多信息Reactjs Documentation

编辑: 仅检查完整代码现在您可以拥有任意多的行,您可以获取数据,并且是否已检查,状态为组件

【讨论】:

  • 这不会给我所选项目的行数据。此外,我不是fields: [false, false, false, false] 的粉丝,因为我可以有一张包含一长串数据的表格。
  • @ZeroDarkThirty 这就是我在你的问题why the dataSet is declared in renderRow ? 中问的原因,因为如果数据可以处于你的状态,那么获取数据会容易得多
  • 在实际项目中数据存储在全局Context中。
  • 对于第二点,您可以使用:new Array(length).fill(false) 来创建您的数组
  • 这将动态生成一个[false, false, false, false]数组
猜你喜欢
  • 1970-01-01
  • 2014-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 2018-05-06
  • 1970-01-01
相关资源
最近更新 更多