如果初始排序完成,使用排序数组反应 useState 不会导致重新渲染 SemanticUI 表 [英] React useState with sorted array does not cause re-render of SemanticUI table if initial sorting is done
问题描述
我有一个带有 SemanticUI 的表格,它呈现公司列表,并允许根据单击表格标题重新排列列表.我以为我已经完成了这个工作(开发环境向我发送了一个预先排序的列表),但是在编写测试时,我意识到我不能保证列表是预先排序的.
所以我编辑了我最初的 useState
逻辑,使其具有与标题点击相同的逻辑的排序功能:
I have a table with SemanticUI that renders a list of companies and allows the list to be resorted based on clicking the header of the table. I thought I had this wrapped up working fine (the dev env is sending me a pre-sorted list), but when writing tests, I realized I cannot guarantee the list is pre-sorted.
So I edited my initial useState
logic to have a sort function which is the same logic for the header click:
之前(作品):const [data, setData] = useState(companies);
之后(不起作用):const [data, setData] = useState(companySortData(companys, COLUMN_NAME.NAME));
上述两个初始 UI 看起来都不错,但是当我单击标题时,这就是出现问题的地方:UI 没有改变(重新渲染).
The initial UI looks OK with both above, but when I click the header, this is where the issue appears: the UI does not change (re-render).
在我逐步执行代码时,我看到正确重新排序的数组在那里,但在 React 内部的某处,该数组不会触发状态更改.
In my stepping through the code, I see that the correctly RE-sorted array is there, but somewhere in the React internals, the array does not trigger a state change.
我尝试了多种选择,包括:1) 更改状态,使 data
成为 sortedColumnData
的一部分,2) 制作 useEffect
钩子,允许初始公司数据在那里,然后我们setData
.3) 尝试创建一个 usePrevious
钩子来比较 sortedColumnData
,然后在依赖项改变时调用 setData
.4) 从 Functional 改为 PureComponent,但这也不能完全解决.
I have tried a plethora of options including: 1) changing the state so that the data
is part of the sortedColumnData
, 2) making an useEffect
hook that allows for the initial company data to be there and then we setData
. 3) trying to create a usePrevious
hook that compares the sortedColumnData
and then calls setData
if that dependency changes. 4) changed from a Functional to PureComponent, but this isn't fully solving it either.
我知道 React 只是浅层差异,所以这可能是它的一部分,但数据数组形状本身很浅,所以我认为这会被选中(如果我不对数据进行初始排序,就会这样做):
I know that React only shallow diffs so that might be part of it, but the data array shape itself is shallow so I thought this would be picked up (which is does if I do not initial sort data):
{
id: "newnew",
name: "A New New Company",
subdomain: "newnew",
createdAt: "2019-10-28T21:54:30.253Z",
updatedAt: "2019-10-28T21:54:30.253Z"
},
所以,这是一个 CodeSandBox 链接:https://codesandbox.io/s/wonderful-汤普森-vg2x1
So, here is a CodeSandBox link: https://codesandbox.io/s/wonderful-thompson-vg2x1
这是组件本身:
export const CompanyTable = ({ companies }) => {
const [sortedColumnData, setSortedColumnData] = useState({
data: companySortData(companies, COLUMN_NAME.NAME), // swap this initial sorting with just the `companies` and this table works fine
direction: DIRECTION.ASCENDING,
columnName: COLUMN_NAME.NAME
});
const handleClick = clickedColumn => {
if (!clickedColumn) return;
if (sortedColumnData.columnName !== clickedColumn) {
setSortedColumnData({
data: companySortData(sortedColumnData.data, clickedColumn),
columnName: clickedColumn,
direction: DIRECTION.ASCENDING
});
return;
}
setSortedColumnData({
data: sortedColumnData.data.reverse(),
columnName: clickedColumn,
direction:
sortedColumnData.direction === DIRECTION.ASCENDING
? DIRECTION.DESCENDING
: DIRECTION.ASCENDING
});
};
return (
<Table celled selectable sortable>
<Table.Header>
<Table.Row>
{headerCells.map(cell => (
<Table.HeaderCell
key={`cell-${cell.testid}`}
sorted={
sortedColumnData.columnName === cell.clickHandlerText
? sortedColumnData.direction
: undefined
}
data-testid={`header-${cell.testid}`}
onClick={() => handleClick(cell.clickHandlerText)}
>
{cell.title}
</Table.HeaderCell>
))}
</Table.Row>
</Table.Header>
<Table.Body data-testid="company-table-body">
{sortedColumnData.data.map(
({ id, name, subdomain, createdAt, updatedAt }) => {
return (
<Table.Row
key={id}
role="link"
data-testid={id}
style={{ cursor: "pointer" }}
>
<Table.Cell singleLine>{name}</Table.Cell>
<Table.Cell singleLine>{subdomain}</Table.Cell>
<Table.Cell singleLine>{createdAt}</Table.Cell>
<Table.Cell singleLine>{updatedAt}</Table.Cell>
</Table.Row>
);
}
)}
</Table.Body>
</Table>
);
};
我不确定为什么使用重新排序的数组从设置状态重新渲染失败...
I am not sure why the re-rendering from setting state with the re-sorted array is failing...
推荐答案
发生这种情况是因为您将 props 的引用传递给 sort 函数并尝试将其设置为状态,您不能改变 prop,这是 React 的基础.如果要将道具存储到状态,请先杀死引用.希望它能解答您的疑问:)
Its happening because you passed the reference of your props to sort function and trying to set it in state, you cannot mutate a prop, its the fundamental of React. If you want to store a prop into state kill the reference first. Hope it answers your doubt :)
const companySortData = (data, sortBy) => {
let copyData = JSON.parse(JSON.stringify(data));
return copyData.sort((a, b) => {
let x = a[sortBy];
let y = b[sortBy];
if (sortBy === COLUMN_NAME.CREATED || sortBy === COLUMN_NAME.UPDATED) {
x = new Date(a.createdAt);
y = new Date(b.createdAt);
} else {
x = a[sortBy].toLowerCase();
y = b[sortBy].toLowerCase();
}
return x < y ? -1 : x > y ? 1 : 0;
});
};
这篇关于如果初始排序完成,使用排序数组反应 useState 不会导致重新渲染 SemanticUI 表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!