仅当两个依赖项都更改时才运行效果挂钩

Posted

技术标签:

【中文标题】仅当两个依赖项都更改时才运行效果挂钩【英文标题】:Run Effect hook only when both dependencies change 【发布时间】:2019-11-14 19:46:12 【问题描述】:

我有一个 React 组件,它使用 useEffect 钩子获取数据,如下所示:

const cache = key: "data-fetched-using-key"
function Config(key, options) 
    const [data, setData] = useState();
    useEffect(() => 
        const fetchedData; // fetch data using key and options
        setData(fetchedData);
        cache[key] = fetchedData;
    , [key, options])
    return <p>data</p>;

每次keyoptions 更改时都会运行挂钩。但是,我也在本地缓存数据,并且只希望在 keyoptions 更改时运行效果(因为对于每个键/选项组合,数据将始终相同)。

有没有一种干净的方式来依赖 keyoptions 而不是 keyoptions 使用 React Hooks 的组合?

【问题讨论】:

是的,有。你需要使用 useCallback 钩子。 【参考方案1】:

您可以使用useRef() 创建这种逻辑。考虑以下示例和沙箱:https://codesandbox.io/s/react-hooks-useeffect-with-multiple-reqs-6ece5

const App = () => 
  const [name, setName] = useState();
  const [age, setAge] = useState();

  const previousValues = useRef( name, age );

  useEffect(() => 
    if (
      previousValues.current.name !== name &&
      previousValues.current.age !== age
    ) 
      //your logic here
      console.log(name + " " + age);
      console.log(previousValues.current);

      //then update the previousValues to be the current values
      previousValues.current =  name, age ;
    
  );

  return (
    <div>
      <input
        placeholder="name"
        value=name
        onChange=e => setName(e.target.value)
      />
      <input
        placeholder="age"
        value=age
        onChange=e => setAge(e.target.value)
      />
    </div>
  );
;

工作流程:

    我们为要跟踪的两个值创建一个ref 对象, 在这种情况下,它是 nameage。引用对象是previousValuesuseEffect 已定义,但我们不提供任何依赖项。 相反,只要有状态更改,我们就让它执行 nameage。 现在在useEffect 内部我们有条件逻辑来检查是否 nameage 的先前/初始值都不同于 它们对应的状态值。如果他们很好,我们执行 我们的逻辑(console.log)。 最后在执行完逻辑后,将引用对象(previousValues)更新为当前值(state)

【讨论】:

【参考方案2】:

为了在两个值都发生变化时运行效果,您需要利用之前的值并在键或选项发生变化时在钩子中比较它们。

您可以编写一个usePrevious 挂钩并比较本文中提到的旧状态和以前的状态:

How to compare oldValues and newValues on React Hooks useEffect?

function usePrevious(value) 
  const ref = useRef();
  useEffect(() => 
    ref.current = value;
  );
  return ref.current;


const cache = key: "data-fetched-using-key"
function Config(key, options) 
    const [data, setData] = useState();
    const previous = usePrevious(key, options);
    useEffect(() => 
        if(previous.key !== key && previous.options !== options) 
            const fetchedData; // fetch data using key and options
            setData(fetchedData);
            cache[key] = fetchedData;
        
    , [key, options])
    return <p>data</p>;

【讨论】:

很好,我也很喜欢这个。 :) 这太好了,谢谢!肯定会记得 usePrevious :)【参考方案3】:

所有提供的解决方案都很好,但是有一些更复杂的情况,例如,只有当依赖项 A 和 B 发生变化时才应该调用 useEffect 函数,而它也取决于 C 的值。

所以我建议使用 useEffects 和中间状态的序列来为未来的逻辑提供更多空间。对所问问题的这种方法的实施将是:

const cache = key: "data-fetched-using-key"
function Config(key, options) 
    const [data, setData] = useState();

    const [needsUpdate, setNeedsUpdate] = useState(()=>(key:false, option:false));

    useEffect(()=>
      setNeedsUpdate((needsUpdate)=>(...needsUpdate, key:true));
    ,[key])

    useEffect(()=>
      setNeedsUpdate((needsUpdate)=>(...needsUpdate, options:true));
    ,[options])

    useEffect(() => 
      if (needsUpdate.key && needsUpdate.options)
        const fetchedData; // fetch data using key and options
        setData(fetchedData);
        cache[key] = fetchedData;
        setNeedsUpdate(()=>(key:false, option:false));
      
    , [needsUpdate, key, options])
    return <p>data</p>;


通过这种方式,我们几乎可以在 useEffect 依赖项上应用任何逻辑,但是它也有自己的缺点,就是渲染周期要少。

【讨论】:

以上是关于仅当两个依赖项都更改时才运行效果挂钩的主要内容,如果未能解决你的问题,请参考以下文章

仅当 Rails 中的属性发生更改时才运行回调

仅当页面的某个部分发生更改时才运行角度编译

仅当输入中的所有元素都更改时,如何使 useEffect 挂钩调用提供的效果?

仅当 pr 目标为 master 时才运行 GitHub 操作

当所有依赖项都发生变化时使用效果?

仅当状态改变时才使用效果