当所有依赖项都改变时使用效果?

Jac*_*ack 8 reactjs react-hooks

当前useEffect仅在依赖项之一发生更改时触发。

当两个(或所有)依赖项都发生变化时,我如何更新它/使用它来回火?

Aus*_*rst 8

当所有依赖项都发生变化时,您需要添加一些逻辑来调用您的效果。这useEffectAllDepsChange应该可以实现您想要的行为。

这里的策略是将之前的 deps 与当前的 deps 进行比较。如果它们不是全部不同,我们将之前的 deps 保留在 ref 中,并且在它们不同之前不要更新它。这允许您在调用效果之前多次更改 deps。

import React, { useEffect, useState, useRef } from "react";

// taken from https://usehooks.com/usePrevious/
function usePrevious(value) {
  const ref = useRef();

  useEffect(() => {
    ref.current = value;
  }, [value]);
  
  return ref.current;
}

function useEffectAllDepsChange(fn, deps) {
  const prevDeps = usePrevious(deps);
  const changeTarget = useRef();

  useEffect(() => {
    // nothing to compare to yet
    if (changeTarget.current === undefined) {
      changeTarget.current = prevDeps;
    }

    // we're mounting, so call the callback
    if (changeTarget.current === undefined) {
      return fn();
    }

    // make sure every dependency has changed
    if (changeTarget.current.every((dep, i) => dep !== deps[i])) {
      changeTarget.current = deps;

      return fn();
    }
  }, [fn, prevDeps, deps]);
}

export default function App() {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);

  useEffectAllDepsChange(() => {
    console.log("running effect", [a, b]);
  }, [a, b]);

  return (
    <div>
      <button onClick={() => setA((prev) => prev + 1)}>A: {a}</button>
      <button onClick={() => setB((prev) => prev + 1)}>B: {b}</button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

编辑充满活力的天空-q9hju

由 Richard 启发的另一种方法更简洁,但缺点是在更新中渲染更多。

function useEffectAllDepsChange(fn, deps) {
  const [changeTarget, setChangeTarget] = useState(deps);

  useEffect(() => {
    setChangeTarget(prev => {
      if (prev.every((dep, i) => dep !== deps[i])) {
        return deps;
      }

      return prev;
    });
  }, [deps]);

  useEffect(fn, changeTarget);
}
Run Code Online (Sandbox Code Playgroud)

  • 第二种方法更干净。 (2认同)

Ric*_*sen 5

我喜欢@AustinBrunkhorst 的灵魂,但你可以用更少的代码来做到这一点。

使用仅在满足您的条件时更新的状态对象,并将其设置在第二个 useEffect 中。

import React, { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);
  const [ab, setAB] = useState({a, b});

  useEffect(() => {
    setAB(prev => {
      console.log('prev AB', prev)
      return (a !== prev.a && b !== prev.b) 
        ? {a,b} 
        : prev;  // do nothing
    })
  }, [a, b])

  useEffect(() => {
    console.log('both have changed')
  }, [ab])

  return (
    <div className="App">
      <div>Click on a button to increment its value.</div>
      <button onClick={() => setA((prev) => prev + 1)}>A: {a}</button>
      <button onClick={() => setB((prev) => prev + 1)}>B: {b}</button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

编辑relaxed-https-w5grz