删除行时更新单元格背景颜色。 (反应-table)
Update cell background color when deleting the row. (react-table)
我正在使用 react-table 库在其 7.6.2 版 中渲染 table .
table(是分页的 table)具有添加或删除行以及编辑单元格值的功能。
每次用户添加新行或编辑单元格时,单元格或行都会更新为蓝色背景。
到目前为止,一切正常。删除行时会出现问题。
从数据结构中删除该行后,该行从table中移除,但是该行的颜色保留在table中,直到更新分页.
我的生产应用程序使用 redux,但我创建了一个简化的 sandbox 来重现错误。
我已验证 table数据 已正确更新。
import React, { useState, useEffect, useMemo } from 'react'
import PropTypes from 'prop-types'
import Notifier from 'components/common/Notifier'
import ContextMenu from './ContextMenu'
import CustomTable, { header } from './customTable'
import colorSelector from './coloring'
const SequenceTable = ({ tableData = [], sTool = null, sPart = null, onRowSelect = () => {}, onUpdateTableData = () => {}, handlePartChange = () => {} }) => {
const columns = useMemo(() => header, [])
const [skipPageReset, setSkipPageReset] = useState(false)
const [selectedRow, setSelectedRow] = useState(null)
const [mousePos, setMousePos] = useState({ x: null, y: null })
const [contextRow, setContextRow] = useState(null)
const updateMyData = (rowIndex, columnId, value) => {
setSkipPageReset(true)
onUpdateTableData({ rowIndex: rowIndex, columnId: columnId, value: value })
}
const handleContextMenuOpen = (event, row) => {
event.preventDefault()
setMousePos({ x: event.clientX, y: event.clientY })
setContextRow(row.values)
}
const handleContextMenuClose = () => {
setContextRow(null)
setMousePos({ x: null, y: null })
}
useEffect(() => {
onRowSelect(selectedRow)
}, [selectedRow])
useEffect(() => {
if (tableData != null && tableData.length !== 0) handlePartChange(sTool, tableData[0])
}, [sPart])
useEffect(() => setSkipPageReset(false), [sTool, sPart])
return (
<React.Fragment>
<CustomTable
columns={columns}
data={tableData}
updateMyData={updateMyData}
openContextMenu={handleContextMenuOpen}
setSelectedRow={setSelectedRow}
skipPageReset={skipPageReset}
getCellProps={cellInfo => colorSelector(cellInfo.value ? cellInfo.value.colorCode : -1)}
/>
<ContextMenu mousePos={mousePos} row={contextRow} onClose={() => handleContextMenuClose()} />
<Notifier />
</React.Fragment>
)
}
SequenceTable.propTypes = {
tableData: PropTypes.array,
sTool: PropTypes.string,
sPart: PropTypes.string
}
export default SequenceTable
import React, { useEffect } from 'react'
import { useTable, usePagination, useSortBy, useRowSelect } from 'react-table'
import Table from 'react-bootstrap/Table'
import ClickAndHold from 'components/common/ClickAndHold'
import EditableCell from './EditableCell'
import Pagination from './Pagination'
const defaultColumn = { Cell: EditableCell }
const CustomTable = ({ columns, data, updateMyData, openContextMenu, setSelectedRow, skipPageReset, getCellProps = () => ({}) }) => {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
selectedFlatRows,
state: { pageIndex, pageSize, selectedRowIds }
} = useTable(
{
columns,
data,
stateReducer: (newState, action) => {
if (action.type === 'toggleRowSelected') {
newState.selectedRowIds = {
[action.id]: true
}
}
return newState
},
defaultColumn,
autoResetPage: !skipPageReset,
updateMyData,
initialState: {
sortBy: [
{
id: 'id',
desc: false
}
],
hiddenColumns: ['id']
}
},
useSortBy,
usePagination,
useRowSelect
)
useEffect(() => {
if (selectedFlatRows.length !== 0) setSelectedRow(selectedFlatRows[0].original)
}, [setSelectedRow, selectedRowIds])
return (
<React.Fragment>
<Table responsive striped bordered hover size="sm" {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<ClickAndHold id={i} elmType={'tr'} onHold={e => openContextMenu(e, row)} {...row.getRowProps()} onContextMenu={e => openContextMenu(e, row)}>
{row.cells.map(cell => {
return <td {...cell.getCellProps([getCellProps(cell)])}>{cell.render('Cell')}</td>
})}
</ClickAndHold>
)
})}
</tbody>
</Table>
<Pagination
canPreviousPage={canPreviousPage}
canNextPage={canNextPage}
pageOption={pageOptions}
pageCount={pageCount}
gotoPage={gotoPage}
nextPage={nextPage}
previousPage={previousPage}
setPageSize={setPageSize}
pageIndex={pageIndex}
pageSize={pageSize}
/>
</React.Fragment>
)
}
export default CustomTable
我的自定义单元格组件:
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import InputBase from '@material-ui/core/InputBase'
import { openSnackbar } from 'components/common/Notifier'
const EditableCell = (
{
value: initialValue,
row: { index },
column: { id },
updateMyData // This is a custom function that we supplied to our table instance
},
{ literal = () => '' }
) => {
const [isValid, setIsValid] = useState(true)
const [value, setValue] = useState(initialValue)
const [errorMsg, setErrorMsg] = useState('')
const [edited, setEdited] = useState(false)
const onChange = e => {
e.persist()
setEdited(true)
let valid = true
if (value.type === 'bool' && e.target.value !== 'true' && e.target.value !== 'false') {
console.log('mustBeBoolean')
valid = false
}
if (value.type === 'number' && isNaN(e.target.value)) {
console.log('mustBeNumeric')
valid = false
}
setValue(oldVal => {
return Object.assign({}, oldVal, {
value: e.target.value
})
})
setIsValid(valid)
}
const onBlur = () => {
if (isValid) {
if (edited) updateMyData(index, id, value.value)
} else {
setValue(initialValue)
value.value != null && openSnackbar({ message: errorMsg, apiResponse: 'error' })
}
setEdited(false)
}
useEffect(() => {
setValue(initialValue)
}, [initialValue])
return <InputBase disabled={!value.editable} value={value.value != null ? value.value : ''} onChange={onChange} onBlur={onBlur} />
}
EditableCell.contextTypes = {
literal: PropTypes.func
}
export default EditableCell
我的数据模型如下:
const data =[{
"id": 1,
"absltBendingStep": {
"value": 2,
"editable": false,
"colorCode": -1,
"type": "number"
},
"rltvBendingStep": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"circInterpolation": {
"value": null,
"editable": true,
"colorCode": -1,
"type": "bool"
},
"shape": {
"value": null,
"editable": true,
"colorCode": -1,
"type": "bool"
},
"xClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"tip": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"headUpperClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"headLowerClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"duPlate": {
"value": 15.75706,
"editable": true,
"colorCode": -1,
"type": "number"
},
"xConf": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"yConf": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"angle": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"description": {
"value": "15.8",
"editable": false,
"colorCode": -1,
"type": "string"
},
"upperClamp": {
"value": 0,
"editable": false,
"colorCode": -1,
"type": "number"
},
"time": {
"value": 0,
"editable": false,
"colorCode": -1,
"type": "number"
},
"observations": {
"value": "",
"editable": false,
"colorCode": -1,
"type": "string"
}
},
{
"id": 2,
"absltBendingStep": {
"value": 3,
"editable": false,
"colorCode": -1,
"type": "number"
},
"rltvBendingStep": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"circInterpolation": {
"value": null,
"editable": true,
"colorCode": -1,
"type": "bool"
},
"shape": {
"value": null,
"editable": true,
"colorCode": -1,
"type": "bool"
},
"xClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"tip": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"headUpperClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"headLowerClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"duPlate": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"xConf": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"yConf": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"angle": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"description": {
"value": "",
"editable": false,
"colorCode": -1,
"type": "string"
},
"upperClamp": {
"value": 0,
"editable": false,
"colorCode": -1,
"type": "number"
},
"time": {
"value": 0,
"editable": false,
"colorCode": -1,
"type": "number"
},
"observations": {
"value": "",
"editable": false,
"colorCode": -1,
"type": "string"
}
}]
这个问题的主要原因是按键。要了解有关如何使用密钥的更多信息,您可以在此处查看 React 的官方文档:https://reactjs.org/docs/lists-and-keys.html#keys
造成这种不一致的主要原因是这里的代码
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<ClickAndHold id={i} elmType={'tr'} onHold={e => openContextMenu(e, row)} {...row.getRowProps()} onContextMenu={e => openContextMenu(e, row)}>
{row.cells.map(cell => {
return <td {...cell.getCellProps([getCellProps(cell)])}>{cell.render('Cell')}</td>
})}
</ClickAndHold>
)
})}
</tbody>
ClickAndHold
组件从 row.getRowProps()
传递了 props。 row.getRowProps()
returns 包含看起来像 row_0
的键的对象。现在,此键取决于行在 table 中的位置。假设有五行,那么它们的键就是row_0
、row_1
、row_2
、row_3
和row_4
。如果删除第 4 行(键 row_3
),第五行(键 row_4
)将获得第四行的键。假设您实际上删除了第四行,那么键将如下所示:row_0
、row_1
、row_2
、row_3
。所以,现在,第五行(以前有键 row_4
,但现在有键 row_3
)具有第四行的键。因此,当 react 重新渲染你的树时,它会将第四行的道具传递给第五行。这意味着如果第四行有蓝色背景,那么第五行也会有蓝色背景。我知道这是少数,但我希望我在这里说得通。
要解决此问题,您需要将唯一键传递给该行。理想情况下,这个唯一键应该来自您正在呈现的数据。如果我查看您的数据,您的 id
是独一无二的。因此,使用此 id
作为 ClickAndHold
组件的键。总结一下,要解决这个问题,您需要将代码编辑为
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<ClickAndHold id={i} elmType={'tr'} onHold={e => openContextMenu(e, row)} {...row.getRowProps()} key={row.original.id} onContextMenu={e => openContextMenu(e, row)}>
{row.cells.map(cell => {
return <td {...cell.getCellProps([getCellProps(cell)])}>{cell.render('Cell')}</td>
})}
</ClickAndHold>
)
})}
page
列表中的 row
对象包含一个包含您的数据的 original
对象。因此,您只需使用自定义数据中的 id
,并将其用作 key
。您需要在 {...row.getRowProps()}
之后传递 key
以覆盖 row.getRowProps()
.
返回的密钥
我已经在你的 codesandbox 中测试过了,你只需要用这种方式编辑 CustomTable.jsx
中第 85 行中找到的 tr
组件。
<tr
id={i}
{...row.getRowProps()}
key={row.original.id}
onContextMenu={(e) => openContextMenu(e, row)}
>
希望对您有所帮助。
另一个建议,在您添加新行的代码中,您正在更改新添加行之后的所有 ID。这是参考代码。
setTableData((sequence) => {
const newData = sequence.reduce((acc, step) => {
if (incrementId) {
step.id = step.id + 1;
acc.push(step);
} else {
acc.push(step);
}
if (step.id === nextToId) {
newStep.id = newStep.id + 1;
acc.push(newStep);
incrementId = true;
}
return acc;
}, []);
return newData;
});
这将导致不一致,因为当您更改用作行的键的 id
时,在下一次重新渲染时,React 会将 props 传递给属于新添加的前一行的新添加的行添加行替换。要了解更多信息,请查看这篇文章:https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318. What I'm trying to say is, keys should be unique for every component inside the list, and if a component is added or deleted, any other component cannot take its key. You can use uuid for generating unique keys. Take a look here for how to use uuid https://www.npmjs.com/package/uuid.
本质上,您需要小心处理键,否则您可能会严重降低应用程序的性能,或搞乱组件树的状态。
更新
抱歉,我错了这个问题的根本原因。虽然您使用按键的方式确实存在问题,但背景颜色问题并不仅仅是因为它。其实导致背景颜色不变的原因是你设置了background-color: none
。 background-color
没有名为 none 的 属性。所以,这是一个无效的 CSS 属性,它不会在 DOM 中更新。这会导致以前有效的背景颜色徘徊,从而导致问题。要解决此问题,您可能需要在要删除蓝色背景时设置 background-color: unset
或 background-color: white
。希望对您有所帮助!
我正在使用 react-table 库在其 7.6.2 版 中渲染 table .
table(是分页的 table)具有添加或删除行以及编辑单元格值的功能。 每次用户添加新行或编辑单元格时,单元格或行都会更新为蓝色背景。
到目前为止,一切正常。删除行时会出现问题。 从数据结构中删除该行后,该行从table中移除,但是该行的颜色保留在table中,直到更新分页.
我的生产应用程序使用 redux,但我创建了一个简化的 sandbox 来重现错误。
我已验证 table数据 已正确更新。
import React, { useState, useEffect, useMemo } from 'react'
import PropTypes from 'prop-types'
import Notifier from 'components/common/Notifier'
import ContextMenu from './ContextMenu'
import CustomTable, { header } from './customTable'
import colorSelector from './coloring'
const SequenceTable = ({ tableData = [], sTool = null, sPart = null, onRowSelect = () => {}, onUpdateTableData = () => {}, handlePartChange = () => {} }) => {
const columns = useMemo(() => header, [])
const [skipPageReset, setSkipPageReset] = useState(false)
const [selectedRow, setSelectedRow] = useState(null)
const [mousePos, setMousePos] = useState({ x: null, y: null })
const [contextRow, setContextRow] = useState(null)
const updateMyData = (rowIndex, columnId, value) => {
setSkipPageReset(true)
onUpdateTableData({ rowIndex: rowIndex, columnId: columnId, value: value })
}
const handleContextMenuOpen = (event, row) => {
event.preventDefault()
setMousePos({ x: event.clientX, y: event.clientY })
setContextRow(row.values)
}
const handleContextMenuClose = () => {
setContextRow(null)
setMousePos({ x: null, y: null })
}
useEffect(() => {
onRowSelect(selectedRow)
}, [selectedRow])
useEffect(() => {
if (tableData != null && tableData.length !== 0) handlePartChange(sTool, tableData[0])
}, [sPart])
useEffect(() => setSkipPageReset(false), [sTool, sPart])
return (
<React.Fragment>
<CustomTable
columns={columns}
data={tableData}
updateMyData={updateMyData}
openContextMenu={handleContextMenuOpen}
setSelectedRow={setSelectedRow}
skipPageReset={skipPageReset}
getCellProps={cellInfo => colorSelector(cellInfo.value ? cellInfo.value.colorCode : -1)}
/>
<ContextMenu mousePos={mousePos} row={contextRow} onClose={() => handleContextMenuClose()} />
<Notifier />
</React.Fragment>
)
}
SequenceTable.propTypes = {
tableData: PropTypes.array,
sTool: PropTypes.string,
sPart: PropTypes.string
}
export default SequenceTable
import React, { useEffect } from 'react'
import { useTable, usePagination, useSortBy, useRowSelect } from 'react-table'
import Table from 'react-bootstrap/Table'
import ClickAndHold from 'components/common/ClickAndHold'
import EditableCell from './EditableCell'
import Pagination from './Pagination'
const defaultColumn = { Cell: EditableCell }
const CustomTable = ({ columns, data, updateMyData, openContextMenu, setSelectedRow, skipPageReset, getCellProps = () => ({}) }) => {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
selectedFlatRows,
state: { pageIndex, pageSize, selectedRowIds }
} = useTable(
{
columns,
data,
stateReducer: (newState, action) => {
if (action.type === 'toggleRowSelected') {
newState.selectedRowIds = {
[action.id]: true
}
}
return newState
},
defaultColumn,
autoResetPage: !skipPageReset,
updateMyData,
initialState: {
sortBy: [
{
id: 'id',
desc: false
}
],
hiddenColumns: ['id']
}
},
useSortBy,
usePagination,
useRowSelect
)
useEffect(() => {
if (selectedFlatRows.length !== 0) setSelectedRow(selectedFlatRows[0].original)
}, [setSelectedRow, selectedRowIds])
return (
<React.Fragment>
<Table responsive striped bordered hover size="sm" {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<ClickAndHold id={i} elmType={'tr'} onHold={e => openContextMenu(e, row)} {...row.getRowProps()} onContextMenu={e => openContextMenu(e, row)}>
{row.cells.map(cell => {
return <td {...cell.getCellProps([getCellProps(cell)])}>{cell.render('Cell')}</td>
})}
</ClickAndHold>
)
})}
</tbody>
</Table>
<Pagination
canPreviousPage={canPreviousPage}
canNextPage={canNextPage}
pageOption={pageOptions}
pageCount={pageCount}
gotoPage={gotoPage}
nextPage={nextPage}
previousPage={previousPage}
setPageSize={setPageSize}
pageIndex={pageIndex}
pageSize={pageSize}
/>
</React.Fragment>
)
}
export default CustomTable
我的自定义单元格组件:
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import InputBase from '@material-ui/core/InputBase'
import { openSnackbar } from 'components/common/Notifier'
const EditableCell = (
{
value: initialValue,
row: { index },
column: { id },
updateMyData // This is a custom function that we supplied to our table instance
},
{ literal = () => '' }
) => {
const [isValid, setIsValid] = useState(true)
const [value, setValue] = useState(initialValue)
const [errorMsg, setErrorMsg] = useState('')
const [edited, setEdited] = useState(false)
const onChange = e => {
e.persist()
setEdited(true)
let valid = true
if (value.type === 'bool' && e.target.value !== 'true' && e.target.value !== 'false') {
console.log('mustBeBoolean')
valid = false
}
if (value.type === 'number' && isNaN(e.target.value)) {
console.log('mustBeNumeric')
valid = false
}
setValue(oldVal => {
return Object.assign({}, oldVal, {
value: e.target.value
})
})
setIsValid(valid)
}
const onBlur = () => {
if (isValid) {
if (edited) updateMyData(index, id, value.value)
} else {
setValue(initialValue)
value.value != null && openSnackbar({ message: errorMsg, apiResponse: 'error' })
}
setEdited(false)
}
useEffect(() => {
setValue(initialValue)
}, [initialValue])
return <InputBase disabled={!value.editable} value={value.value != null ? value.value : ''} onChange={onChange} onBlur={onBlur} />
}
EditableCell.contextTypes = {
literal: PropTypes.func
}
export default EditableCell
我的数据模型如下:
const data =[{
"id": 1,
"absltBendingStep": {
"value": 2,
"editable": false,
"colorCode": -1,
"type": "number"
},
"rltvBendingStep": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"circInterpolation": {
"value": null,
"editable": true,
"colorCode": -1,
"type": "bool"
},
"shape": {
"value": null,
"editable": true,
"colorCode": -1,
"type": "bool"
},
"xClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"tip": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"headUpperClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"headLowerClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"duPlate": {
"value": 15.75706,
"editable": true,
"colorCode": -1,
"type": "number"
},
"xConf": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"yConf": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"angle": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"description": {
"value": "15.8",
"editable": false,
"colorCode": -1,
"type": "string"
},
"upperClamp": {
"value": 0,
"editable": false,
"colorCode": -1,
"type": "number"
},
"time": {
"value": 0,
"editable": false,
"colorCode": -1,
"type": "number"
},
"observations": {
"value": "",
"editable": false,
"colorCode": -1,
"type": "string"
}
},
{
"id": 2,
"absltBendingStep": {
"value": 3,
"editable": false,
"colorCode": -1,
"type": "number"
},
"rltvBendingStep": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"circInterpolation": {
"value": null,
"editable": true,
"colorCode": -1,
"type": "bool"
},
"shape": {
"value": null,
"editable": true,
"colorCode": -1,
"type": "bool"
},
"xClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"tip": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"headUpperClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"headLowerClamp": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "string"
},
"duPlate": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"xConf": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"yConf": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"angle": {
"value": null,
"editable": false,
"colorCode": -1,
"type": "number"
},
"description": {
"value": "",
"editable": false,
"colorCode": -1,
"type": "string"
},
"upperClamp": {
"value": 0,
"editable": false,
"colorCode": -1,
"type": "number"
},
"time": {
"value": 0,
"editable": false,
"colorCode": -1,
"type": "number"
},
"observations": {
"value": "",
"editable": false,
"colorCode": -1,
"type": "string"
}
}]
这个问题的主要原因是按键。要了解有关如何使用密钥的更多信息,您可以在此处查看 React 的官方文档:https://reactjs.org/docs/lists-and-keys.html#keys
造成这种不一致的主要原因是这里的代码
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<ClickAndHold id={i} elmType={'tr'} onHold={e => openContextMenu(e, row)} {...row.getRowProps()} onContextMenu={e => openContextMenu(e, row)}>
{row.cells.map(cell => {
return <td {...cell.getCellProps([getCellProps(cell)])}>{cell.render('Cell')}</td>
})}
</ClickAndHold>
)
})}
</tbody>
ClickAndHold
组件从 row.getRowProps()
传递了 props。 row.getRowProps()
returns 包含看起来像 row_0
的键的对象。现在,此键取决于行在 table 中的位置。假设有五行,那么它们的键就是row_0
、row_1
、row_2
、row_3
和row_4
。如果删除第 4 行(键 row_3
),第五行(键 row_4
)将获得第四行的键。假设您实际上删除了第四行,那么键将如下所示:row_0
、row_1
、row_2
、row_3
。所以,现在,第五行(以前有键 row_4
,但现在有键 row_3
)具有第四行的键。因此,当 react 重新渲染你的树时,它会将第四行的道具传递给第五行。这意味着如果第四行有蓝色背景,那么第五行也会有蓝色背景。我知道这是少数,但我希望我在这里说得通。
要解决此问题,您需要将唯一键传递给该行。理想情况下,这个唯一键应该来自您正在呈现的数据。如果我查看您的数据,您的 id
是独一无二的。因此,使用此 id
作为 ClickAndHold
组件的键。总结一下,要解决这个问题,您需要将代码编辑为
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<ClickAndHold id={i} elmType={'tr'} onHold={e => openContextMenu(e, row)} {...row.getRowProps()} key={row.original.id} onContextMenu={e => openContextMenu(e, row)}>
{row.cells.map(cell => {
return <td {...cell.getCellProps([getCellProps(cell)])}>{cell.render('Cell')}</td>
})}
</ClickAndHold>
)
})}
page
列表中的 row
对象包含一个包含您的数据的 original
对象。因此,您只需使用自定义数据中的 id
,并将其用作 key
。您需要在 {...row.getRowProps()}
之后传递 key
以覆盖 row.getRowProps()
.
我已经在你的 codesandbox 中测试过了,你只需要用这种方式编辑 CustomTable.jsx
中第 85 行中找到的 tr
组件。
<tr
id={i}
{...row.getRowProps()}
key={row.original.id}
onContextMenu={(e) => openContextMenu(e, row)}
>
希望对您有所帮助。
另一个建议,在您添加新行的代码中,您正在更改新添加行之后的所有 ID。这是参考代码。
setTableData((sequence) => {
const newData = sequence.reduce((acc, step) => {
if (incrementId) {
step.id = step.id + 1;
acc.push(step);
} else {
acc.push(step);
}
if (step.id === nextToId) {
newStep.id = newStep.id + 1;
acc.push(newStep);
incrementId = true;
}
return acc;
}, []);
return newData;
});
这将导致不一致,因为当您更改用作行的键的 id
时,在下一次重新渲染时,React 会将 props 传递给属于新添加的前一行的新添加的行添加行替换。要了解更多信息,请查看这篇文章:https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318. What I'm trying to say is, keys should be unique for every component inside the list, and if a component is added or deleted, any other component cannot take its key. You can use uuid for generating unique keys. Take a look here for how to use uuid https://www.npmjs.com/package/uuid.
本质上,您需要小心处理键,否则您可能会严重降低应用程序的性能,或搞乱组件树的状态。
更新
抱歉,我错了这个问题的根本原因。虽然您使用按键的方式确实存在问题,但背景颜色问题并不仅仅是因为它。其实导致背景颜色不变的原因是你设置了background-color: none
。 background-color
没有名为 none 的 属性。所以,这是一个无效的 CSS 属性,它不会在 DOM 中更新。这会导致以前有效的背景颜色徘徊,从而导致问题。要解决此问题,您可能需要在要删除蓝色背景时设置 background-color: unset
或 background-color: white
。希望对您有所帮助!