Dependency array

2 snippets in React

REReact

Dependency Array

RE · useEffect
syntax
useEffect(effectFn, [dep1, dep2]);
// Empty array: mount only
useEffect(effectFn, []);
// No array: every render
useEffect(effectFn);
example
function SearchResults({ query, page }) {
  const [results, setResults] = useState([]);

  useEffect(() => {
    // Runs whenever query or page changes
    let cancelled = false;

    async function search() {
      const data = await fetchResults(query, page);
      if (!cancelled) setResults(data);
    }
    search();

    return () => { cancelled = true; };
  }, [query, page]);

  return <ResultsList items={results} />;
}

Note React uses Object.is to compare dependencies. Objects and arrays create new references each render, so they will always trigger the effect. Extract primitive values or memoize objects before listing them as dependencies.

Dependency Arrays

RE · useMemo & useCallback
syntax
// All values from component scope used inside must be listed
useCallback(() => fn(a, b), [a, b]);
useMemo(() => compute(x), [x]);
example
function ChatRoom({ roomId, serverUrl }) {
  // Recreated only when roomId or serverUrl change
  const connect = useCallback(() => {
    const ws = new WebSocket(`${serverUrl}/rooms/${roomId}`);
    ws.onopen = () => console.log('Connected to', roomId);
    return ws;
  }, [roomId, serverUrl]);

  useEffect(() => {
    const ws = connect();
    return () => ws.close();
  }, [connect]);

  return <div>Chat: {roomId}</div>;
}

Note Missing a dependency leads to stale closures. Including an unnecessary one causes pointless recalculations. Use the exhaustive-deps lint rule (eslint-plugin-react-hooks) to catch mistakes automatically.