自定义使用效果第二个参数

时间:2018-12-03 21:12:01

标签: javascript reactjs

新的React API包含useEffect(),其第二个自变量采用Object,React进行比较以查看组件是否已更新。

例如

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

其中[props.source]是所讨论的参数。

我的问题是:我可以定义一个自定义函数来运行以检查道具是否已更改吗?

我有一个自定义对象,React似乎无法告诉它何时已更改。

3 个答案:

答案 0 :(得分:1)

AFAIK,目前无法实现。有一些解决方法:

1)在useEffect内部手动进行深度比较。保存上一个。您可以使用useState或什至更好的useRef值,如此处所示:https://overreacted.io/a-complete-guide-to-useeffect/

2)使用JSON.stringify(props.source)进行哈希处理。如果数据不是太大,可以的。请注意,stringify可能会产生不一致的结果(对象更改顺序中的键将更改输出)。

3)使用md5(props.source)进行哈希处理(或其他一些快速/轻度哈希处理)。比以前更容易实现,但速度较慢。

答案 1 :(得分:0)

该答案的积分转到@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>

为什么使用useRef代替useState

通过使用useState返回的函数,组件将被重新渲染,在这种情况下,这是不需要的

答案 2 :(得分:0)

从其他答案中汲取灵感,我一直在使用以下钩子

import React from 'react';

import IsEqual from 'lodash/isEqual';

/**
 * The role of this function is to ensure that useEffect doesn't not
 * fire unless the value of a varr (variable but "var" is a reserved word ?)
 * changes. For examples:
 * const a = { 'hi': 5 }
 * const b = { 'hi': 5 }
 *
 * a === b // false
 * IsEqual(a, b) // true
 *
 * By default the React.useEffect(() => {}, [dependencies]) only does "===" but
 * for cases where we only want useEffect to re-fire when the true value
 * of a varr changes "IsEqual", we use this.
 *
 * @param varr: any
 * @returns {function(): *}
 */
const useMostRecentImmutableValue = (varr) => {
  let mostRecentValue = varr;
  const mostRecentPointer = React.useRef(varr);

  return () => {
    // short circuit if "shallow equality"
    if (mostRecentPointer.current === varr) {
      return mostRecentValue;
    }

    // mostRecentValue only updates when the true value of varr changes
    if (!IsEqual(varr, mostRecentPointer.current)) {
      mostRecentValue = varr;
    }

    // mostRecentPointer changes every time "shallow equality" fails but
    // after we've checked deep equality
    mostRecentPointer.current = varr;

    return mostRecentValue;
  };
};

export default useMostRecentImmutableValue;

然后在一个钩子中我会做这样的事情:

import useMostRecentImmutableValue from 'use-most-recent-immutable-value.hook';

const useMyHook = (foo = { 'hi': 5 }) => {
  const mostRecentFoo = useMostRecentImmutableValue(foo);

  React.useEffect(() => {
    const unsubscribe = mySubscriptionFunction(mostRecentFoo);
    return () => {
      unsubscribe();
    };
  }, [mostRecentFoo]);

};

export default useMyHook;