Javascript 反应 useEffect 比较对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/54095994/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:11:53  来源:igfitidea点击:

react useEffect comparing objects

javascriptreactjsreact-hooks

提问by peter flanagan

I am using react useEffecthooks and checking if an object has changed and only then run the hook again.

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

My code looks like this.

我的代码看起来像这样。

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

    return {
        data
    };
};

Unfortunately it keeps running as the objects are not being recognised as being the same.

不幸的是,它一直在运行,因为对象没有被识别为相同的。

I believe the following is an example of why.

我相信以下是原因的一个例子。

const objA = {
   method: 'GET'
}

const objB = {
   method: 'GET'
}

console.log(objA === objB)

Perhaps running JSON.stringify(apiOptions)works?

也许跑步JSON.stringify(apiOptions)有效?

回答by lenilsondc

Use apiOptionsas state value

使用apiOptions的状态值

I'm not sure how you are consuming the custom hook but making apiOptionsa state value by using useStateshould work just fine. This way you can serve it to your custom hook as a state value like so:

我不确定您是如何使用自定义钩子的,但是apiOptions通过使用创建状态值useState应该可以正常工作。通过这种方式,您可以将其作为状态值提供给您的自定义钩子,如下所示:

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

This way it's going to change only when you use setApiOptions.

这样它只会在您使用setApiOptions.

Example #1

示例#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>;
}

Alternatively

或者

You could write a deep comparable useEffectas described 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, useDeepCompareMemoize(dependencies))
}

You can use it like you'd use useEffect.

您可以像使用useEffect.

回答by Steffi

I just found a solution which works for me.

我刚刚找到了一个对我有用的解决方案。

You have to use usePrevious()and _.isEqual()from Lodash. Inside the useEffect(), you put a condition if the previousapiOptionsequals to the currentapiOptions. If true, do nothing. If false updateData().

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

Example :

例子 :

const useExample = (apiOptions) => {

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

usePrevious(value)is a custom hook which create a refwith useRef().

usePrevious(value)是一个自定义钩子,它创建一个refwith useRef()

You can found it from the Official React Hook documentation.

您可以从官方 React Hook 文档 中找到它。

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

回答by kasongoyo

If you're real sure that you cannot control apiOptionsthen just replace native useEffect with https://github.com/kentcdodds/use-deep-compare-effect.

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