仅当两个依赖项都发生变化时才运行 Effect 钩子

IT技术 reactjs react-hooks
2021-03-30 19:05:32

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

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更改时都会运行钩子但是,我也在本地缓存数据,并且只希望在keyANDoptions更改时运行效果(因为对于每个键/选项组合,数据将始终相同)。

有没有一种干净的方法来依赖于keyANDoptions而不是keyORoptions使用 React Hooks 的组合?

3个回答

您可以使用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>
  );
};

工作流程:

  1. 我们ref为要跟踪的两个值创建一个对象,在本例中为 anameageref 对象是previousValues.
  2. useEffect已定义,但我们不提供任何依赖项。相反,我们只是让它在name的状态发生变化时执行 age
  3. 现在在里面useEffect我们有条件逻辑来检查两者的先前/初始值nameage是否与其对应的状态值不同。如果它们很好,我们就会执行我们的逻辑(console.log)
  4. 最后在执行逻辑后,将 ref 对象更新(previousValues)为当前值(state)

为了在两个值更改时运行效果,您需要利用之前的值并在键或选项更改时在钩子内比较它们。

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

如何在 React Hooks useEffect 上比较 oldValues 和 newValues?

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>;
}
不错,我也很喜欢这个。:)
2021-05-31 19:05:32
这太好了,谢谢!肯定会记住 usePrevious :)
2021-06-13 19:05:32

所有提供的解决方案都很好,但是有一些更复杂的情况,例如,当 useEffect 函数应仅在依赖项 A 和 B 发生变化时才应调用,而它也取决于 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 依赖项,但是它有自己的缺点,即渲染周期较少。