I'm creating a sortable table, and my issue is that the sorting shows after TWO clicks on a column, instead of just after one click.
This is my sorting logic: (DATA is hardcoded)
export const useFetch = (order) => {
const [data, setData] = useState();
const orderBy = order?.by || 'offer_id';
const fromTo = order?.fromTo || SORTING_ORDER.ascending;
useEffect(() => {
if (!orderBy || !fromTo) return;
let orderedData = DATA.sort((a, b) => (a[orderBy] - b[orderBy]));
setData(orderedData);
}, [orderBy, fromTo]);
return { data, status };
};
And I'm using this hook like this, from the component that has that table.
export const AcceptedOffers = ({ setModalIsOpen }) => {
const [order, setOrder] = useState({ by: 'maturity', fromTo: SORTING_ORDER.ascending });
const { data, status } = useFetch(order);
function onHeaderClick(header) {
setOrder({ by: header, fromTo: SORTING_ORDER.descending });
}
return (
<WidgetContainer>
<Title>
Accepted Offers
</Title>
<Table>
<Header>
<tr>
{
Object.entries(HEADERS).map(([key, value]) =>
<th
key={key}
onClick={() => onHeaderClick(key)}
>
{value}
</th>)
}
</tr>
</Header>
<Body>
{
data?.map(row => (<tr key={row.offer_id}>
<td>{row.offer_id}</td>
etc...
Can anyone explain what's wrong with this. Thank you.
CodePudding user response:
The side effect represented by your useEffect
is executed after the render triggered by the click: the data are rendered first, then sorted. That's where your "delay" commes from.
Here is an other solution. It may suit you, or not: the purpose is to show an alternative implementation to trigger the sort when order is modified, but without useEffect
. It works by "overloading" setOrder:
export const useFetch = (initialOrder) => {
// useReducer may be a better choice here,
// to store order and data with a single state
// (and update this state through a single call)
const [order, setOrder] = useState(initialOrder);
const [data, setData] = useState();
const publicSetOrder = (newOrder) => {
setOrder(newOrder);
const orderBy = newOrder?.by || 'offer_id';
const fromTo = newOrder?.fromTo || SORTING_ORDER.ascending;
if (!orderBy || !fromTo) return;
let orderedData = DATA.sort((a, b) => (a[orderBy] - b[orderBy]));
setData(orderedData);
};
return { order, setOrder: publicSetOrder, data, status };
};
export const AcceptedOffers = ({ setModalIsOpen }) => {
const { order, setOrder, data, status } = useFetch({ by: 'maturity', fromTo: SORTING_ORDER.ascending });
function onHeaderClick(header) {
setOrder({ by: header, fromTo: SORTING_ORDER.descending });
}
// ...
Feel free to adapt to your use case ;)