web-dev-qa-db-ja.com

オブジェクトの比較useEffect

私は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
    };
};
_

残念ながら、オブジェクトが同じものとして認識されていないため、実行を続けます。

以下がその一例だと思います。

_const objA = {
   method: 'GET'
}

const objB = {
   method: 'GET'
}

console.log(objA === objB)_

おそらくJSON.stringify(apiOptions)を実行するとうまくいきますか?

36
peter flanagan

apiOptionsを状態値として使用

カスタムフックをどのように使用しているかはわかりませんが、apiOptionsを使用してuseStateを状態値にすることで問題なく動作します。このようにして、次のような状態値としてカスタムフックに提供できます。

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>;
}

あるいは

説明されているように、深い比較可能なuseEffectを書くことができます here

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, useDeepCompareMemoize(dependencies))
}

useEffectと同じように使用できます。

20
lenilsondc

私はうまくいく解決策を見つけました。

LodashのusePrevious()および_.isEqual()を使用する必要があります。 useEffect()内に、previousapiOptionscurrentapiOptions。 trueの場合、何もしません。 falseの場合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を作成するカスタムフックです。

公式Reactフックのドキュメント から見つけることができます。

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

apiOptionsを制御できないことが確かな場合は、ネイティブのuseEffectを https://github.com/kentcdodds/use-deep-compare-effect に置き換えてください。

2
kasongoyo