I have a dropdown menu in my app. When it is selected it triggers a method that changes the sortOption state, which then triggers a useEffect to sort the data (called allTabs). However, the data always seems to be a step behind. For example, the default is "new to old", but it initially displays the data unsorted. If a new option is then pressed, say "A-Z", the data then becomes sorted by "new to old". If a new option is then pressed again, say "old to new", the data then becomes sorted by "A-Z" and etc. I am unsure of why this is happening. I have seen posts about using callbacks but I'm pretty new to React, Node.js, and Typescript and I'm unsure how to integrate those answers into my code.
Here is what the dropdown looks like:
{/* Dropdown for sorting */}
<div className="mb-4">
<label htmlFor="sortOptions" className="mr-2">Sort by:</label>
<select
style={{ color: 'black' }}
id="sortOptions"
value={sortOption}
onChange={handleSortChange}
className="p-2 border rounded"
>
<option value="Newest to Oldest">Newest to Oldest</option>
<option value="Oldest to Newest">Oldest to Newest</option>
<option value="A-Z">A-Z</option>
<option value="Z-A">Z-A</option>
</select>
</div>
Here is what the handleSortChange callback is:
// Handle dropdown change with type annotation
const handleSortChange = (event: ChangeEvent<HTMLSelectElement>) => {
setSortOption(event.target.value);
};
Here is the useState of sortOption, and the useEffect that is triggered by a change in sortOption or allTabs (the data I am sorting):
const [sortOption, setSortOption] = useState<string>("Newest to Oldest");
// sort tabs based on selected option, update if allTabs or sortOption changes
useEffect(() => {
console.log(allTabs);
console.log(sortOption);
switch (sortOption) {
case "Newest to Oldest":
allTabs.sort(compareDates);
console.log("sorted new to old");
break;
case "Oldest to Newest":
allTabs.sort((a, b) => compareDates(b, a));
console.log("sorted old to new");
break;
case "A-Z":
allTabs.sort((a, b) => a.name.localeCompare(b.name));
console.log("sorted A-Z");
break;
case "Z-A":
allTabs.sort((a, b) => b.name.localeCompare(a.name));
console.log("sorted Z-A");
break;
default:
return;
}
console.log(allTabs);
}, [sortOption, allTabs])
I know that the methods within this useEffect work properly, and so do the sorts.
As you can see, there are console logs in the useEffect method. They print out the data and the path taken. They have given me an interesting result. When I print allTabs before and after the switch, they are both sorted according to the proper path that is printed out, meaning everything looks correct. The option selected by the user is indeed what is printed, and how the data becomes sorted. But the data that is actually displayed on the website is still of the data from the last selection. I therefore assume that the render of the data is not updating when allTabs is updated, which I need to be able to do. If I try to actually use the setter of allTabs in the useEffect, it just causes an infinite loop, so I'm unsure how I could get react to realize the data has been updated. A force reload of the page just resets everything including the data and sort option.
To display the data I am currently doing a map of allTabs. Should I instead do this in a component and make it take in some data so that I can call this component with the data whenever I change it or something?
This is how the data is displayed. This occurs within a return at the bottom of the app, within a <main> with some other stuff:
{allTabs.map((allTab, index) => (
<React.Fragment key={index}>
<Link
className=""
href="/"
onClick={() => {
localStorage.setItem("tabTitle", allTab.name);
localStorage.setItem("tabContent", allTab.tab);
}}
>
{allTab.name}
</Link>
<span>
{allTab.created_by + " on " + formatDate(allTab.created_at)}
</span>
<div className="ml-auto">
{savedTabs.some(
(savedTab) => savedTab.tabs.id === allTab.id
) ? (
<Image
src="/saved.png"
alt="saved tab"
className="dark:invert"
width={24}
height={24}
onClick={() => {
toggleSaved(userId, allTab);
}}
/>
) : (
<Image
src="/not_saved.png"
alt="unsaved tab"
className="dark:invert"
width={24}
height={24}
onClick={() => {
toggleSaved(userId, allTab);
}}
priority
/>
)}
</div>
</React.Fragment>
))}