1

I have an issue where my state is only set on the second click. Ive been reading about lifting up the state which Ive tried to implement but I am still running into the same issue with this button. There are multiple components that the state is passed through and maybe thats the issue I could use some help understanding how to resolve this issue Ill try and outline in the simplest way:

I have my parent table component:

const TableView = ({...all the props}) => { const [sort, setSortBy] = useState({ sortBy: "", order: "", }); const handleOnsortChange = (values) => { setSortBy(values); trimData(items.sort(sorter), pageNumber, 50); }; // sort function const sorter = (a, b) => { ...} return ( <Table> <TableHeader handleOnsortChange={handleOnsortChange} sort={sort} /> ... ) 

Table Header Component:

const TableHeader = ({ handleOnsortChange, sort }) => { const onSortChange = (value) => { handleOnsortChange(value); }; return ( <thead className="payroll-list-header"> <tr> <th> <span>Name</span> <SortView onChange={onSortChange} type={"fullName"} sort={sort} /> <div className="arrow"></div> </th> ... ) 

Sorter Component:

const SortView = ({ onChange, type, sort: { sortBy, order } }) => { const onClick = (order) => { onChange({ sortBy: type, order }); }; return ( <div className="sort"> <div onClick={() => onClick("asc")} className="icon-up"> <Octicon icon={TriangleUp} className={`${order === "asc" && sortBy == type ? "active" : ""}`} ></Octicon> </div> <div onClick={() => onClick("desc")} className="icon-down"> <Octicon icon={TriangleDown} className={`${order === "desc" && sortBy == type ? "active" : ""}`} ></Octicon> </div> </div> ); }; 

UPDATE: TrimData & sorter function: outside of these functions the state for sort is updated but inside when i console sort its empty.

//sort providers const sorter = (a, b) => { const nameA = a.FullName.toUpperCase(); const nameB = b.FullName.toUpperCase(); let compare = 0; if (sort.sortBy === "fullName") { if (nameA > nameB) { compare = 1; } else if (nameA < nameB) { compare = -1; } if (sort.order === "asc") { return compare; } else if (sort.order === "desc") { return compare * -1; } }; 
 const trimData = (items, pageNumber, rows) => { const pages = items.length > 0 ? Math.ceil(items.length / rows) : 1; const trimStart = (pageNumber - 1) * rows; const trimEnd = trimStart + rows; const trimedData = items.slice(trimStart, trimEnd); setEndRange(trimEnd); setTrimedData(trimedData); setNumberOfPages(pages); }; 

You can see sort is passed through to the sort component and set in the parent. Ive tried setting it in the descendant components but that didn't help. Also passing setSortBy offered the same result. I hope this isnt to big of a code chunk and someone can offer advice to a junior dev :)

3
  • 1
    Are you sure the state remains the same, and it's not the sorting itself that doesn't happen? It might be that this is because the initial sort is already what you try to change it to. Commented Aug 11, 2021 at 17:39
  • 2
    Which part of sorting is not working? The code you've provided is only showing the sort table "headers". Are the "headers" (Octicon) showing the right order and sortBy values? If so, the state of the sort value is being set properly. Is the actual sorting itself not working? If so please provide detail on what trimData is doing and what items are. I only ask because to me it looks like everything is being passed down in a way that it should update properly. Commented Aug 11, 2021 at 17:58
  • 1
    After looking into further I realize the state is being set but for some reason the updated state is not reflected in my helper functions. ie trimData and sorter. I will add them into the question Commented Aug 11, 2021 at 19:03

1 Answer 1

2

The reason sorting is only working on the second click is because on the first click, while the sort value updates, the sort used by trimData function is the old sort object before the click was made.

So let's think about it, sorter is a function with a sort object that is set to {order: "", sortBy: ""} on its first render. When you click on a button, this is the sort object that is being used by your sorter function on your items.sort. Updating sort in setSortBy will then update the components with the new value, and the components will rerender. When the component has rerendered, the sorter function is using the sort that was just set, lets say {order: "asc", sortBy: "fullName"} (but that function hasn't yet been called). When you click a second time, let's say on the desc now, then the function is called with the sort object that was used to render the component i.e. the {order: "asc", sortBy: "fullName"} one, not the sort object that you passed to update the state {order: "desc", sortBy: "fullName"}.

Trying not to be too confusing as there are many approaches you can take, the most direct option you have is to make the "sorter" function using sort values you want to update your state with before you pass it into your items.sort(...):

const handleOnsortChange = (values) => { // `values` is what you want the next `sort` value to be. setSortBy(values); // but `sort` wont be updated yet, so just use `values` instead. const sorterWithCorrectSort = (a, b) => { const nameA = a.FullName.toUpperCase(); const nameB = b.FullName.toUpperCase(); let compare = 0; if (values.sortBy === "fullName") { // "sort" renamed to "values" if (nameA > nameB) { compare = 1; } else if (nameA < nameB) { compare = -1; } if (values.order === "asc") { // "sort" renamed to "values" return compare; } else if (values.order === "desc") { // "sort" renamed to "values" return compare * -1; } }; trimData(items.sort(sorterWithCorrectSortValues), pageNumber, 50); } 

There are better ways to format the code, but I believe this is at the heart of the issue. Your sort object in your sorter function is the one that was used to render the component, not the one you used to update the state with.

Sign up to request clarification or add additional context in comments.

2 Comments

Wow thank you for explaining that and showing a solution. This works for me, I didnt think to put the sorter function inside the handle sort. Thanks very much!
@CourtneyJ No problem!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.