skip to Main Content

I run into this more and more frequently in various forms: (simplified example)

const searchParameters = useState();
const sorting = useState();

// update searchParameters if the user enters someting
// update sorting if the user clicks on a column in the table

// PROBLEM: I only want to call loadEntries() if sorting changes, but I need searchParameters for it too!
useEffect(
    () => loadEntries(sorting, searchParameters),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [sorting]
);
  1. Is there a way to deal with this situation without having to disable react-hooks/exhaustive-deps?
  2. (less important) is there a way to disable react-hooks/exhaustive-deps for only a specific dependency? (searchParameters in this case)

2

Answers


  1. You can use a ref to hold the current value of searchParameters, and since the ref itself doesn’t change (only the current property) you don’t even need to state it as a dependency:

    const searchParameters = useState();
    const sorting = useState();
    
    // init the ref
    const searchParametersRef = useRef(searchParameters);
    
    // update the ref
    useEffect(() => {
      searchParametersRef.current = searchParameters;
    });
    
    useEffect(
      () => loadEntries(sorting, searchParametersRef.current), // use the ref
      [sorting]
    );
    

    As far as I know, you can’t disable react-hooks/exhaustive-dep for a specific property like searchParameters. The configuration only deals with the rules.

    Login or Signup to reply.
  2. The answer from @OriDrori would address the question.

    The following is an alternate solution, not based on ref. Certainly it comes at a cost of an extra state and a separate useEffect statement.

    Coding highlights

    a. An extra state for sorting

      const [prevSorting, setPrevSorting] = useState();
    

    b. Two separate useEffect invocations

    The first useEffect will invoke for every render, but the logic inside will be applied with respect to the changes in sorting state.

    The second useEffect will invoke only on the changes in sorting state, and will keep preSorting state in synch.

      useEffect(() => {
        if (sorting !== prevSorting) {
          loadEntries(sorting, searchParameters);
        }
      }); 
    
    
      useEffect(() => {
        setPrevSorting(sorting);
      }, [sorting]);
    

    Code – full listing:

    App.js

    import { useState, useEffect } from 'react';
    
    function loadEntries(sorting, searchParameters) {
      console.log(`loadEntries executed with : ${sorting},${searchParameters}`);
    }
    
    export default function App() {
      const [searchParameters, setSearchParameters] = useState('');
      const [sorting, setSorting] = useState();
      const [prevSorting, setPrevSorting] = useState();
    
      // update searchParameters if the user enters someting
      function handleChangeSearchParameters(newValue) {
        setSearchParameters(newValue);
      }
      // update sorting if the user clicks on a column in the table
      function handleClickSorting(newValue) {
        setSorting(newValue);
      }
    
      // PROBLEM: I only want to call loadEntries() if sorting changes, but I need searchParameters for it too!
      useEffect(() => {
        if (sorting !== prevSorting) {
          loadEntries(sorting, searchParameters);
        }
      });
    
      useEffect(() => {
        setPrevSorting(sorting);
      }, [sorting]);
    
      return (
        <>
          <button onClick={() => handleClickSorting(Math.random())}>
            Click sorting
          </button>
          <br />
          <label>searchParameters</label>
          <br />
          <input
            value={searchParameters}
            onChange={(e) => handleChangeSearchParameters(e.target.value)}
          ></input>
        </>
      );
    }
    

    Test runs

    A search parameter newly entered

    It did not invoke useEffect for the changes in Sorting, please see there is nothing logged in the console

    Browser display - A search parameter newly entered

    A change in Sorting by clicking the button

    It did invoke useEffect for the changes in Sorting, please see there is something logged in the console.

    Browser display - A change in sorting

    A change in the search parameter

    It did not invoke useEffect for changes in Sorting, please see there is nothing newly logged in the console.

    Browser display - A change in search parameter

    A change in Sorting by clicking the button

    It did invoke useEffect for changes in Sorting, please see there is something newly logged in the console.

    Browser display - Another change in sorting

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search