skip to Main Content

I am trying to use a select element to set internal state to either ‘ascending’ or ‘descending’. Once the state is set, I would like to use that state value to conditionally sort A-Z or Z-A with orderBy from the lodash package. I know the sort method works because I have console logged them, but for some reason my handleSort function is not sorting my array of items at all.

Here is my code:

import { useState } from "react";
import { orderBy } from "lodash";

const Subjects = () => {
  const { data } = useQuery(GET_SUBJECTS);
  const [sortState, setSortState] = useState("-");

  let sortedSubjects = data.subjects;

  const handleSort = (e) => {
    setSortState(e.target.value);

    if (sortState === "ascending") {
      sortedSubjects = orderBy(data.subjects, "name", "asc");
    } else if (sortState === "descending") {
      sortedSubjects = orderBy(data.subjects, "name", "desc");
    }
  };

  return (
    <>
      <select
        onChange={handleSort}
        defaultValue="default"
        className="home-page-filter"
      >
        <option value="default">-</option>
        <option value="ascending">A-Z</option>
        <option value="descending">Z-A</option>
      </select>
      <h2>Subject</h2>
      
      <div className="subjects-container">
        {sortedSubjects.map((subject) => (
          <SubjectRow key={subject.id} subject={subject} />
        ))}
      </div>
    </>
  );
};

export default Subjects;

I’ve tried putting the sort function directly in the JSX wondering if it was some sort of hoisting issue. I’m still a junior dev, so it might be something very simple and somewhat obvious that I’m missing or doing wrong here. Thank you for any help!

2

Answers


  1. Move the if block outside of handleSort and into a useEffect hook:

    
        useEffect(() => {
            if (sortState === "ascending") {
              sortedSubjects = orderBy(data.subjects, "name", "asc");
            } else if (sortState === "descending") {
              sortedSubjects = orderBy(data.subjects, "name", "desc");
            }
        }, [sortState])
    
    

    see the react docs for useEffect

    Login or Signup to reply.
  2. The sortedSubjects is computed using data and sortState. This means that you need to compute it on each render.

    Compute sortedSubjects each the body of the component. Whenever the component is re-rendered it would be re-computed. If you want to avoid doing it on each render, wrap it with useMemo.

    In addition, instead use asc and desc as the options value, which would shorten you code a bit (see comments in code).

    Because you have the array (data) and the sortState

    const Subjects = () => {
      const { data } = useQuery(GET_SUBJECTS);
      const [sortState, setSortState] = useState("default");
      
      // compute sortedSubject whenever data.subjects or sortState changes
      const sortedSubjects = useMemo(() => 
        sortState === "default" 
        ? data.subjects // return unsort if deafult
        : orderBy(data.subjects, "name", sortState) // sort by sortState
      , [data.subjects, sortState]);
    
      const handleSort = (e) => {
        setSortState(e.target.value);
      };
    
      return (
        <>
          <select
            onChange={handleSort}
            defaultValue="default"
            className="home-page-filter"
          >
            <option value="default">-</option>
    
            {/* change the values to asc/desc */}
            <option value="asc">A-Z</option>
            <option value="desc">Z-A</option>
          </select>
          <h2>Subject</h2>
          
          <div className="subjects-container">
            {sortedSubjects.map((subject) => (
              <SubjectRow key={subject.id} subject={subject} />
            ))}
          </div>
        </>
      );
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search