Reputation: 404
I am trying to display some data into the table
element using react.
The table will have 5 columns and 9 rows
.
I am using a state which holds the data to be displayed in the table as array of arrays,
const [displayedSlot, setDisplayedSlot] = useState(
hours.map(() => days.map(() => ""))
);
where,
const days = ["Mon", "Tue", "Wed", "Thu", "Fri"];
const hours = [
"8:40-9:40",
"9:40-10:40",
"10:40-11:40",
"11:40-12:40",
"12:40-13:40",
"13:40-14:40",
"14:40-15:40",
"15:40-16:40",
"17:40-18:40",
];
I also have two additional states as well,
const [possibleSchedules, setPossibleSchedules] = useState([]);
const [currentSchedule, setCurrentSchedule] = useState(null);
possibleSchedules
holds the data can be displayed in the table(it's filled after some user input), and, currentSchedule
holds the index for which possible schedule will be displayed.
When the user changes the value of the currentSchedule
, I want to clear the table and display new schedule data in the table.
What I have tried initially as follows,
useEffect(() => {
// I was hoping this would clear table before I update it with new data.
setDisplayedSlot(hours.map(() => days.map(() => "")));
const schedule = possibleSchedules[currentSchedule];
// this function calls `setDisplayedSlots` to fill the correct data cells,
// for given `schedule`
updateTable(schedule);
}, [currentSchedule]);
This didn't work as I expected, what happens is, the table isn't cleared and data from the previous schedule data remained if the corresponding cell isn't overridden by the newly selected schedule.
I thought this is due to the asynchronous nature of the react state updates. (?)
To work around this, I have added new state,
const [isTableClear, setIsTableClear] = useState(false);
Changed the previous useEffect
and added another one,
useEffect(() => {
setDisplayedSlot(hours.map(() => days.map(() => ""))); // clears table
setIsTableClear(true);
}, [currentSchedule]);
useEffect(() => {
if (isTableClear) {
const schedule = possibleSchedules[currentSchedule];
updateTable(schedule);
setIsTableClear(false);
}
}, [isTableClear]);
This one works, but I have a feeling, this is a bit of a hack and not a react best practice at all. Since I am fairly new to react, I want to ask if this is a good solution, or how would you solve it?
Upvotes: 2
Views: 65
Reputation: 203587
Any time you are queueing react state updates that depend on previous state then you should use a functional state update.
You are enqueuing multiple state updates from within the same render cycle, each one using the state from the current render cycle, thus overwriting each previous state update.
Since you enqueuing more than a single update to displayedSlot
you need to do a functional update from the first state update. The first state update in the effect is ok since you are "wiping" state clean, subsequent updates should be functional.
useEffect(() => {
setDisplayedSlot(hours.map(() => days.map(() => ""))); // <-- this is ok
const schedule = possibleSchedules[currentSchedule];
updateTable(schedule);
}, [currentSchedule]);
updateTable - Use a functional state update to take in any previous enqueued update to update from.
...
setDisplayedSlot(displayedSlot => {
const updatedDisplayedSlot = displayedSlot.map(
...
// logic to update specific slots
...
);
...
return updatedDisplayedSlot;
});
...
Upvotes: 1