将Including array as a dependency in useEffect
P粉748218846
P粉748218846 2023-10-17 18:50:23
0
2
557

Every 5 seconds there is some data coming from the long poll, and I want my component to dispatch an operation every time an item of the array (or the array length itself) changes. How do I prevent useEffect from going into an infinite loop when passing an array as a dependency, but still manage to schedule some operations if any value changes?

useEffect(() => {
  console.log(outcomes)
}, [outcomes])

Where outcomes is an ID array, such as [123, 234, 3212]. Items in the array may be replaced or removed, so the total length of the array may (but does not necessarily) remain the same, so passing outcomes.length as a dependency is not the case.

outcomes Custom selector from reselect:

const getOutcomes = createSelector(
  someData,
  data => data.map(({ outcomeId }) => outcomeId)
)


P粉748218846
P粉748218846

reply all(2)
P粉464208937

Using JSON.stringify() or any deep comparison method may be less efficient, if you know the shape of the object ahead of time you can write your own effect hook to trigger a callback to your custom equality function based on the result.

useEffect works by checking if each value in the dependencies array is the same as the value in the previous render and executing a callback if one of them is not. Therefore, we only need to use useRef to retain the data instance we are interested in, and only allocate a new instance to trigger the effect when the custom equality check returns false.

function arrayEqual(a1: any[], a2: any[]) {
  if (a1.length !== a2.length) return false;
  for (let i = 0; i  void);

function useNumberArrayEffect(cb: () => MaybeCleanUpFn, deps: number[]) {
  const ref = useRef(deps);

  if (!arrayEqual(deps, ref.current)) {
    ref.current = deps;
  }

  useEffect(cb, [ref.current]);
}

usage

function Child({ arr }: { arr: number[] }) {
  useNumberArrayEffect(() => {
    console.log("run effect", JSON.stringify(arr));
  }, arr);

  return 
{JSON.stringify(arr)}
; }

Going a step further, we can also reuse this hook by creating an effects hook that accepts a custom equality function.

type MaybeCleanUpFn = void | (() => void);
type EqualityFn = (a: DependencyList, b: DependencyList) => boolean;

function useCustomEffect(
  cb: () => MaybeCleanUpFn,
  deps: DependencyList,
  equal?: EqualityFn
) {
  const ref = useRef(deps);

  if (!equal || !equal(deps, ref.current)) {
    ref.current = deps;
  }

  useEffect(cb, [ref.current]);
}

usage

useCustomEffect(
  () => {
    console.log("run custom effect", JSON.stringify(arr));
  },
  [arr],
  (a, b) => arrayEqual(a[0], b[0])
);

Live Demo

P粉662802882

You can pass JSON.stringify(outcomes) as a list of dependencies:

Learn MoreHere

useEffect(() => {
  console.log(outcomes)
}, [JSON.stringify(outcomes)])
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template