可编辑的 React 表:在组件之间传递数据

IT技术 javascript reactjs
2021-04-15 10:22:25

好的,我是 React 的新手,需要帮助阅读/引用 HTML 元素数据属性,甚至是 HTML 标签中的内容 - 通常使用纯 JS(这就是我在 React 应用程序中所做的),我是这样读的:

const postRow = document.getElementById(tableRowId);
    ;
const author = postRow.getElementsByTagName('td')[0].getAttribute('data-fieldvalue');
const title = postRow.getElementsByTagName('td')[1].getAttribute('data-fieldvalue');
const description = postRow.getElementsByTagName('td')[2].getAttribute('data-fieldvalue');

我有一个名为 Table 的功能组件,我像这样使用它:

        <Table
            headers={this.state.postsHeaders}
            rows={this.state.posts}
            editable={this.state.editable}
            deletable={this.state.deletable}
            onUpdateIconClicked={this.toggleUpdatePostModalView}
            onDeleteIconClicked={this.toggleDeletePostModalView}
        />

其中 rows 属性是我正在检索的数据axios我的表生成了多tr > td查找我为每一行都有一个“编辑”CTA,点击后我会打开一个模式,在那里我传递要为每一行编辑的数据。CTA 的 Onclick 调用了这个工作正常的函数:

    toggleUpdatePostModalView = (postId, tableRowId) => {
    // toggle the confirm delete post view
    let showUpdatePostModal = !this.state.showUpdatePostModal;
    // when postId and tableRowId are both null that means
    // that the view delete confirm modal must remain not
    // visible (closed) so have to override the toggle
    if (postId === null && tableRowId === null) {
        showUpdatePostModal = false;
    }

    const postRow = document.getElementById(tableRowId);
    ;
    const author = postRow.getElementsByTagName('td')[0].getAttribute('data-fieldvalue');
    const title = postRow.getElementsByTagName('td')[1].getAttribute('data-fieldvalue');
    const description = postRow.getElementsByTagName('td')[2].getAttribute('data-fieldvalue');
    // dont get the elements directly like above https://reactjs.org/docs/refs-and-the-dom.html

    this.setState({
        ...this.state,
        showUpdatePostModal: showUpdatePostModal,
        postToUpdate: {
            postId: postId,
            tableRowId: tableRowId,
            author: author,
            title: title,
            description: description
        }
    });
}

有人指出,问题是,我不应该用获取数据的方式JS(中getElementByIdgetElementsByTagName,因为虚拟DOM和实际DOM同步问题的功能。所以我会指向https://reactjs.org/docs/refs- and-the-dom.html但如果我tr是一个组件本身,这似乎有效,但实际上,它只是我的 Table 渲染函数中的 HTML,如下所示:

const table = (props) => {

// the following code creates an array from props.header object
// that is an indexed array (0, 1, ..., n) and each value 
// contains the key properties that compose object props.header,
// and so, even though in .map() the parameter says 'key'
// this is misleading because in reality it is the VALUE 
// (since the key of the array is 0 or 1 or n) but it is  called 
// 'key' because it is the key in the props.headers object that we
// need to get the info for (.map(function(currentValue, index, arr))
const headersArray = Object.keys(props.headers);
const tableHeaders = headersArray.map(key => {
    return <th key={key}>{props.headers[key]}</th>;
});
const editHeader = props.editable === true ? <th key="edit">Edit</th> : null;
const deleteHeader = props.deletable === true ? <th key="delete">Delete</th> : null;

let tableRows = null;
if (props.rows) {

    tableRows = props.rows.map((row, key) => {

        return (
            <tr id={`tr-${key}`} key={key}>
                {/* inner loop to dynamically generate the <td>
                    depending on how many headers there are since
                    each header corresponds to a key or column in
                    the table */}
                {headersArray.map(tdKey => {
                    return <td key={tdKey} data-fieldname={tdKey} data-fieldvalue={row[tdKey]} >{row[tdKey]}</td>
                })}

                {props.editable === true ? <td key="edit"><PencilIcon onClick={() => props.onUpdateIconClicked(row.postId, `tr-${key}`)} /></td> : null}
                {props.deletable === true ? <td className="delete-icon-container" key="delete"><TrashIcon onClick={() => props.onDeleteIconClicked(row.postId, `tr-${key}`)} /></td> : null}
            </tr>
        );

    });

}

return (

    <table className="table is-striped">
        <thead>
            <tr>
                {tableHeaders}
                {editHeader}
                {deleteHeader}
            </tr>
        </thead>

        <tbody>
            {tableRows}
        </tbody>
    </table>

);

}

我还读到这些 refs 不应该经常使用 - 那么如果我有一个有 100 行的表怎么办?200?我不确定如何继续并以 React 的方式执行此操作……有人可以帮忙吗?

1个回答

Refs不是在这里使用的合适工具。

相反,你应该提升状态很多)。

为了做到这一点,我建议

  • 打破你的表组件成小块(如<Header /><Row />共同的父内等)<App />和独立<EditDialog />/<DeleteDialog />组件编辑/删除行数据) -更小的组件更容易维护和故障排除
  • 将您的表数据(最好是具有唯一记录id)存储在父 ( <Table />) 组件中,并将与表行相对应的数据条目作为参数传递给<Row />组件
  • 将抽象onEdit()onDelete()事件处理程序作为props传递给<Row />组件并将它们附加到/按钮的onClick()处理程序EditDelete
  • 将这些子props ( onEdit(), onDelete())绑定到将触发编辑/删除对话框的父级中的回调
  • 在记录编辑/删除时相应地更新状态<Table />

这是上述内容的完整演示(我已使用 MaterialUI 进行样式设置,以免使用大量 CSS 使演示负担过重,您可以继续使用自定义组件,希望这不会让我的示例对您不太清楚):

@YevgenGorbunkov 哦,很有道理。我是新来的反应,并没有意识到,state持有修改的属性。非常感谢!
2021-06-01 10:22:25
谢谢 - 我看到您使用的 EditDialog 和 DeleteDialog 只添加了一次,而不是作为表中每一行的组件。这就是我已经在做的事情,很高兴看到我并没有太过分。我会按照你的建议去做。
2021-06-07 10:22:25
@YevgenGorbunkov 很棒的答案。一个问题——为什么要合并recordData以及data何时将参数传递给onSubmitEditonSubmitEdit(...data)因为我们只关心我们修改的状态,这不够吗?
2021-06-20 10:22:25
@muZero:我发布这个答案已经有一段时间了,昨天深夜我回复了你的评论。所以看起来我误导了你。您的状态可能会存储您需要的任何内容。我合并初始行数据和修改后的表单数据的原因useState(recordData)是实际上并没有设置初始data值,因为它指的(即等效于)的先前,所以我真的不想使用设置等于和仅跟踪实际修改的字段。recordDatauseState({})useEffect()datarecordData
2021-06-21 10:22:25