Custom useEffect Second Argument

2020-08-11 10:44发布

The new React API includes useEffect(), the second argument of which takes an Object which React diffs to see if the component updated.

e.g.

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

where [props.source] is the argument in question.

My question is: can I define a custom function to run to check if the prop has changed?

I have a custom object and React can't seem to tell when it has changed.

2条回答
贪生不怕死
2楼-- · 2020-08-11 11:21

Credit for this answer goes to @Tholle use object in useEffect 2nd param without having to stringify it to JSON

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

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

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

    if (isFirst.current || !isSame) {
      fn();
    }

    isFirst.current = false;
    prevDeps.current = deps;
  }, 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"));
<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>

Why use useRef instead of useState

By using the function returned from useState the component will be re-rendered, which is not desired in this case

查看更多
手持菜刀,她持情操
3楼-- · 2020-08-11 11:25

AFAIK it's not currently possible. There are some workarounds:

1) Do deep comparison manually inside useEffect. To store the prev. value you may use useState or, even better, useRef as demonstrated here: https://overreacted.io/a-complete-guide-to-useeffect/

2) Hashing with JSON.stringify(props.source). Can be fine, if the data is not too big. Note that stringify may produce inconsistent results (keys in objects changing order will change the output).

3) Hashing with md5(props.source) (or some other quick/light hashing). More realiable yet slower than the previous.

查看更多
登录 后发表回答