问题
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:
BEFORE (works):
const [data, setData] = useState(companies);
AFTER (does not work):
const [data, setData] = useState(companySortData(companies, COLUMN_NAME.NAME));
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).
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.
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.
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"
},
So, here is a CodeSandBox link: https://codesandbox.io/s/wonderful-thompson-vg2x1
Here is the component itself:
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...
回答1:
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;
});
};
来源:https://stackoverflow.com/questions/59272240/react-usestate-with-sorted-array-does-not-cause-re-render-of-semanticui-table-if