反应 useEffect 比较对象

react useEffect comparing objects

我正在使用 React useEffect 挂钩并检查对象是否已更改,然后才再次 运行 挂钩。

我的代码是这样的。

const useExample = (apiOptions) => {
    const [data, updateData] = useState([]);
    useEffect(() => {
       const [data, updateData] = useState<any>([]);
        doSomethingCool(apiOptions).then(res => {               
           updateData(response.data);
       })
    }, [apiOptions]);

    return {
        data
    };
};

不幸的是,它保持 运行ning,因为对象没有被识别为相同。

我相信以下是原因的示例。

const objA = {
   method: 'GET'
}

const objB = {
   method: 'GET'
}

console.log(objA === objB)

也许 运行宁 JSON.stringify(apiOptions) 有效?

使用apiOptions作为状态值

我不确定您是如何使用自定义挂钩的,但是通过使用 useState 使 apiOptions 成为状态值应该可以正常工作。这样您就可以将它作为状态值提供给您的自定义挂钩,如下所示:

const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions)

这样它只会在您使用 setApiOptions 时改变。

示例 #1

import { useState, useEffect } from 'react';

const useExample = (apiOptions) => {
  const [data, updateData] = useState([]);
  
  useEffect(() => {
    console.log('effect triggered')
  }, [apiOptions]);

  return {
    data
  };
}
export default function App() {
  const [apiOptions, setApiOptions] = useState({ a: 1 })
  const { data } = useExample(apiOptions);
  const [somethingElse, setSomethingElse] = useState('default state')

  return <div>
    <button onClick={() => { setApiOptions({ a: 1 }) }}>change apiOptions</button>
    <button onClick={() => { setSomethingElse('state') }}>
      change something else to force rerender
    </button>
  </div>;
}

或者

您可以按照 here:

的描述写一个深度比较 useEffect
function deepCompareEquals(a, b){
  // TODO: implement deep comparison here
  // something like lodash
  // return _.isEqual(a, b);
}

function useDeepCompareMemoize(value) {
  const ref = useRef() 
  // it can be done by using useMemo as well
  // but useRef is rather cleaner and easier

  if (!deepCompareEquals(value, ref.current)) {
    ref.current = value
  }

  return ref.current
}

function useDeepCompareEffect(callback, dependencies) {
  useEffect(
    callback,
    dependencies.map(useDeepCompareMemoize)
  )
}

您可以像使用 useEffect 一样使用它。

我刚刚找到了适合我的解决方案。

你必须使用 Lodash 的 usePrevious()_.isEqual()。 在 useEffect() 中,如果 previous apiOptions 等于 current apiOptions,则放置一个条件。如果为真,什么都不做。如果为假 updateData().

示例:

const useExample = (apiOptions) => {

     const myPreviousState = usePrevious(apiOptions);
     const [data, updateData] = useState([]);
     useEffect(() => {
        if (myPreviousState && !_.isEqual(myPreviousState, apiOptions)) {
          updateData(apiOptions);
        }
     }, [apiOptions])
}

usePrevious(value) 是一个自定义挂钩,它使用 useRef().

创建 ref

您可以从Official React Hook documentation找到它。

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

如果您确定无法控制 apiOptions,那么只需将原生 useEffect 替换为 https://github.com/kentcdodds/use-deep-compare-effect

如果输入足够浅,以至于您认为深度相等仍然很快,请考虑使用 JSON.stringify:

const useExample = (apiOptions) => {
    const [data, updateData] = useState([]);
    const apiOptionsJsonString = JSON.stringify(apiOptions);

    useEffect(() => {
       const apiOptionsObject = JSON.parse(apiOptionsJsonString);
       doSomethingCool(apiOptionsObject).then(response => {               
           updateData(response.data);
       })
    }, [apiOptionsJsonString]);

    return {
        data
    };
};

注意它不会比较函数。

您可以使用 useDeepCompareEffect、useCustomCompareEffect 或编写您自己的钩子。

https://github.com/kentcdodds/use-deep-compare-effect https://github.com/sanjagh/use-custom-compare-effect

假设您的 apiOptions 的很大一部分是静态的,并且如果组件重新渲染也不会改变,我建议采用以下方法:

const useExample = (apiOptions, deps) => {

  useEffect(callback, deps);

  return { ... };
};

想法是仅传输效果所依赖的值数组作为第二个参数

某些情况下真的很简单!

const objA = {
   method: 'GET'
}

const objB = {
   method: 'GET'
}

console.log(objA === objB) // false

为什么objA不等于objB? Coz JS 只是比较他们的地址,对吗?他们是两个不同的对象。这是我们都知道的!

与 React hooks 一样!

所以,众所周知,objA.method === objB.method 对吗?因为它们是字面意思。

答案出来了:

React.useEffect(() => {
    // do your facy work
}, [obj.method])

另一种选择,如果你有能力修改doSomethingCool

如果确切知道您需要哪些非 Object 属性,您可以将依赖项列表限制为 useEffect 将使用 === 正确解释的属性,例如:

const useExample = (apiOptions) => {
    const [data, updateData] = useState([]);
    useEffect(() => {
       const [data, updateData] = useState<any>([]);
        doSomethingCool(apiOptions.method).then(res => {               
           updateData(response.data);
       })
    }, [apiOptions.method]);

    return {
        data
    };
};