#reactjs #redux #react-hooks #react-table
Вопрос:
Я пытаюсь отсортировать данные в таблице реакций, но по мере поступления новых данных сортировка аннулируется. Компонент, содержащий таблицу, получает свои данные из реквизита. Сами эти данные поступают из redux, который каждые 15 секунд обновляется последними данными с сервера (где они часто меняются).
Что бы я хотел, так это отсортировать данные, и чтобы эти типы оставались на месте по мере/при изменении данных. На самом деле происходит то, что я сортирую таблицу по заголовку столбца, а затем, когда данные меняются, сортировка удаляется.
Я пытался:
- значение
autoResetSortBy
false - реализовано это: https://react-table.tanstack.com/docs/faq#how-do-i-stop-my-table-state-from-automatically-resetting-when-my-data-changes
- прочитайте документы и примеры в поисках очевидных различий
но становится таковым, как только меняются данные:
Я хотел бы сохранить фильтр сортировки, чтобы по мере поступления новых данных (те же 99% времени, но в 1% случаев значение столбца отличается или появляется новая строка) они сортировались с помощью примененного фильтра сортировки.
Вот упрощенный пример моего кода (настолько маленький, насколько я мог его сделать):
import * as React from 'react'
import { useTable, Column, useSortBy } from 'react-table'
import * as SharedTypes from '@shared/declarations'
interface Props {
serverData: SharedTypes.API.MarketBotSummary[]
}
export const TableTest: React.StatelessComponent<Props> = ({ serverData }: Props) => {
const [localData, setLocalData] = React.useState<SharedTypes.API.MarketBotSummary[]>(serverData)
const skipPageResetRef = React.useRef<boolean>(false)
React.useEffect(() => {
skipPageResetRef.current = true
setLocalData(serverData)
console.log('data changed', serverData)
}, [serverData])
React.useEffect(() => {
// After the table has updated, always remove the flag
skipPageResetRef.current = false
})
const Table = ({ columns, data }: { columns: Column<SharedTypes.API.MarketBotSummary>[], data: SharedTypes.API.MarketBotSummary[]}) => {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
// @ts-ignore
state: { sortBy }
} = useTable(
{
columns,
data,
// @ts-ignore
// autoResetSortBy: false,
// autoResetFilters: false,
autoResetPage: !skipPageResetRef.current,
autoResetExpanded: !skipPageResetRef.current,
autoResetGroupBy: !skipPageResetRef.current,
autoResetSelectedRows: !skipPageResetRef.current,
autoResetSortBy: !skipPageResetRef.current,
autoResetFilters: !skipPageResetRef.current,
autoResetRowState: !skipPageResetRef.current,
},
useSortBy
)
// Render the UI for your table
return (
<>
<table {...getTableProps()} className="ui celled very compact structured table">
<thead>
{
// @ts-ignore
headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{
// @ts-ignore
headerGroup.headers.map(column => (
<th {
// @ts-ignore
...column.getHeaderProps(column.getSortByToggleProps())
}>
{column.render('Header')}
<span>
{
// @ts-ignore
column.isSorted ? column.isSortedDesc ? ' 🔽' : ' 🔼' : ''
}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{
// @ts-ignore
rows.map((row) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(
// @ts-ignore
cell => <td {...cell.getCellProps({ className: cell.column?.className?.(cell.value, row.original) })}>
{cell.render('Cell')}
</td>
)}
</tr>
)
})}
</tbody>
</table>
<pre>
<code>
{JSON.stringify(
{
sortBy,
},
null,
2
)}
</code>
</pre>
</>
)
}
const columns = React.useMemo(
() => [
{
Header: 'Data',
columns: [
{
Header: 'Quote',
accessor: 'quote',
},
{
Header: 'Symbol',
accessor: 'symbol',
},
{
Header: 'Mode',
accessor: 'status',
},
{
Header: 'direction',
accessor: 'tradeDirection',
},
],
},
],
[]
)
const data = React.useMemo(
() => localData,
[localData]
)
return (
<Table columns={columns} data={data} />
)
}
export default TableTest
Ответ №1:
Не объявляйте компоненты React внутри других компонентов React. Каждый раз, когда функциональный компонент React повторно отправляет, он воссоздает все , что объявлено в теле его функции, сохраняя при этом ссылки на запоминаемые переменные, поступающие из различных крючков useState
, useCallback
useMemo
и т. Д.
Когда вы объявляете компонент внутри тела другого компонента, вы получаете новый компонент при каждом рендеринге, который не сохранит никакого внутреннего состояния, которое у него было при предыдущем рендеринге. Следовательно, ваши параметры пользовательского интерфейса удаляются.
Объявите Table
в отдельном файле и импортируйте его в TableTest
. В качестве альтернативы, просто переместите все внутри Table
в тело TableTest
.
Я бы также избавился от обоих ваших useMemo
крючков, так как они ничего не достигают. В первом случае, если у вас есть статический массив/объект, просто объявите его вне области действия компонента, в то время как во втором случае localData
он уже эффективно запоминается крючком состояния.
Комментарии:
1. Спасибо, это направило меня в правильном направлении.