Search code examples
reactjsuse-effect

Not understanding UseEffect rendering


In my useEffect, I sort an array based on a users input.
I created an extra useState const [tempFlights, setTempFlights] = React.useState([]); that I only use in my UseEffect. This is a .js file not a .jsx file (I am working on another persons code base). In my useEffect I update setTempFlights and my date for flights updates, when I remove the update for setTempFlights, flights isn't update until the next time the user chooses an input.

Here is my code

import React, { useEffect, useState } from "react";
import "./index.style.css";
import FlightSearchItem from "./FlightSearchItem";
import FlightSearchFooter from "./FlightSearchFooter";
import useFetchFlightResults from "./useFetchFlightResults";
import SortBy from "./filters/SortBy";
import { SortByDefaultOption } from "./filters/SortBy/enums";

export default function FlightSearch() {
  const [sortBy, setSortBy] = useState(SortByDefaultOption);

  // Fetch Flights
  const { flights } = useFetchFlightResults();
  const [tempFlights, setTempFlights] = React.useState([]);

  useEffect(() => {
    let temp = [];
    if (sortBy.value === "PRICE_LOW") {
      temp = flights.sort((a, b) => a.price - b.price);
      setTempFlights([...temp]);
    } else if (sortBy.value === "BEST") {
      temp = flights.sort((a, b) => a.score - b.score);
      setTempFlights([...temp]);
    } else {
      temp = flights.sort((a, b) => {
        return (
          new Date(a.segmentsArray[0].arrivesAt) -
          new Date(b.segmentsArray[0].arrivesAt)
        );
      });
      setTempFlights([...temp]);
    }
  }, [sortBy]);

  // Only show 10 flight results per page
  let paginatedFlights = flights.slice(0, 8);

  return (
    <div className="row">
      <div className="pane m-t-1">
        <div className="pane-header search-results__header">
          <div className="search-results__title">
            <b>Select an outbound flight</b>
            <p className="m-v-0 fade small">DEN → CHI</p>
          </div>
          <SortBy value={sortBy} onChange={setSortBy} />
        </div>
        {/* Display Flight Results */}
        <div className="pane-content">
          {Array.isArray(paginatedFlights) &&
            paginatedFlights.map((flight) => (
              <FlightSearchItem key={flight.id} flight={flight} />
            ))}
        </div>
      </div>
      {/* Pagination */}
      <FlightSearchFooter />
    </div>
  );
}

You can see that the page updates based off of flights not tempFlights. What is going on here and how can I change it to not need setTempFlights([...temp]);


Solution

  • .sort() mutates the original array (flights in this case). This is why you are seeing updates even though you aren't using tempFlights. Oftentimes when using sort, you would create a copy of the original array beforehand to avoid mutating it.

    const tempFlights = [...flights];
    tempFlights.sort()
    

    In the above, tempFlights ends up sorted and flights is left alone.

    If I were rewriting your snippet, I wouldn't use an effect at all. The resulting array can easily be derived from the selected sort value and doesn't need to be held in state separately.

    const [sortBy, setSortBy] = useState(SortByDefaultOption);
    
    // Fetch Flights
    const { flights } = useFetchFlightResults();
    
    const getTempFlights = () => {
        let temp = [...flights];
        if (sortBy.value === "PRICE_LOW") {
          temp.sort((a, b) => a.price - b.price);
        } else if (sortBy.value === "BEST") {
          temp.sort((a, b) => a.score - b.score);
        } else {
          temp.sort((a, b) => {
            return (
              new Date(a.segmentsArray[0].arrivesAt) -
              new Date(b.segmentsArray[0].arrivesAt)
            );
          });
        }
        return temp;
    }
    
    const tempFlights = getTempFlights();
    
    // Render logic as before
    

    You could wrap getTempFlights in a useMemo hook if you're worried about recalculating the array each render, but it is often not consequential with typical data sets