在 useEffect 2nd param 中使用对象而不必将其字符串化为 JSON

rap*_*2-h 20 javascript json reactjs react-hooks

在 JS 中,两个对象不相等。

const a = {}, b = {};
console.log(a === b);
Run Code Online (Sandbox Code Playgroud)

所以我不能在useEffect(React hooks)中使用一个对象作为第二个参数,因为它总是被认为是假的(所以它会重新渲染):

const a = {}, b = {};
console.log(a === b);
Run Code Online (Sandbox Code Playgroud)

这样做(上面的代码),每次组件重新渲染时都会产生运行效果,因为每次都认为对象不相等。

我可以通过将对象作为 JSON 字符串化值传递来“破解”它,但这有点脏 IMO:

function MyComponent() {
  // ...
  useEffect(() => {
    // do something
  }, [myObject]) // <- this is the object that can change.
}
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来做到这一点并避免不必要的效果调用?

旁注:对象具有嵌套属性。效果必须在此对象内的每个更改上运行。

Tho*_*lle 16

您可以创建一个自定义钩子来跟踪 a 中的先前依赖项数组ref,并将对象与例如 Lodash 进行比较,isEqual并且仅在它们不相等时才运行提供的函数。

例子

const { useState, useEffect, useRef } = React;
const { isEqual } = _;

function useDeepEffect(fn, deps) {
  const isFirst = useRef(true);
  const prevDeps = useRef(deps);

  useEffect(() => {
    const isFirstEffect = isFirst.current;
    const isSame = prevDeps.current.every((obj, index) =>
      isEqual(obj, deps[index])
    );

    isFirst.current = false;
    prevDeps.current = deps;

    if (isFirstEffect || !isSame) {
      return fn();
    }
  }, deps);
}

function App() {
  const [state, setState] = useState({ foo: "foo" });

  useEffect(() => {
    setTimeout(() => setState({ foo: "foo" }), 1000);
    setTimeout(() => setState({ foo: "bar" }), 2000);
  }, []);

  useDeepEffect(() => {
    console.log("State changed!");
  }, [state]);

  return <div>{JSON.stringify(state)}</div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常酷的答案,感谢分享。问题是为什么不使用 `useState` 而不是 `useRef`。 (2认同)
  • @ThomasValadez 通过使用从 `useState` 返回的函数,组件将被重新渲染,这在这种情况下是不希望的。 (2认同)