Определите, какая переменная массива зависимостей вызвала ловушку useEffect - PullRequest
9 голосов
/ 15 марта 2019

Есть ли простой способ определить, какая переменная в массиве зависимостей useEffect вызывает перезапуск функции?

Простой выход из системы каждой переменной может вводить в заблуждение, если a - это функция, а b - это объект, они могут выглядеть одинаково при входе в систему, но на самом деле могут отличаться и вызывать срабатывание useEffect.

Например:

React.useEffect(() => {
  // which variable triggered this re-fire?
  console.log('---useEffect---')
}, [a, b, c, d])

Мой текущий метод удалял переменные зависимости по одной, пока я не заметил поведение, которое вызывает чрезмерные вызовы useEffect, но должен быть лучший способ сузить это.

Ответы [ 2 ]

4 голосов
/ 15 марта 2019

Насколько я знаю, нет действительно простого способа сделать это из коробки, но вы могли бы добавить пользовательский хук, который отслеживает его зависимости и регистрирует, какой из них изменился:

// Same arguments as useEffect, but with an optional string for logging purposes
const useEffectDebugger = (func, inputs, prefix = "useEffect") => {
  // Using a ref to hold the inputs from the previous run (or same run for initial run
  const oldInputsRef = useRef(inputs);
  useEffect(() => {
    // Get the old inputs
    const oldInputs = oldInputsRef.current;

    // Compare the old inputs to the current inputs
    compareInputs(oldInputs, inputs, prefix)

    // Save the current inputs
    oldInputsRef.current = inputs;

    // Execute wrapped effect
    func()
  }, inputs);
};

Бит compareInputs может выглядеть примерно так:

const compareInputs = (oldInputs, newInputs, prefix) => {
  // Edge-case: different array lengths
  if(oldInputs.length !== newInputs.length) {
    // Not helpful to compare item by item, so just output the whole array
    console.log(`${prefix} - Inputs have a different length`, oldInputs, newInputs)
    console.log("Old inputs:", oldInputs)
    console.log("New inputs:", newInputs)
    return;
  }

  // Compare individual items
  oldInputs.forEach((oldInput, index) => {
    const newInput = newInputs[index];
    if(oldInput !== newInput) {
      console.log(`${prefix} - The input changed in position ${index}`);
      console.log("Old value:", oldInput)
      console.log("New value:", newInput)
    }
  })
}

Вы можете использовать это так:

useEffectDebugger(() => {
  // which variable triggered this re-fire?
  console.log('---useEffect---')
}, [a, b, c, d], 'Effect Name')

И вы получите вывод вроде:

Effect Name - The input changed in position 2
Old value: "Previous value"
New value: "New value"
3 голосов
/ 15 марта 2019

ОБНОВЛЕНИЕ

После небольшого использования в реальном мире мне пока нравится следующее решение, которое заимствует некоторые аспекты решения Retsam:

const compareInputs = (inputKeys, oldInputs, newInputs) => {
  inputKeys.forEach(key => {
    const oldInput = oldInputs[key];
    const newInput = newInputs[key];
    if (oldInput !== newInput) {
      console.log("change detected", key, "old:", oldInput, "new:", newInput);
    }
  });
};
const useDependenciesDebugger = inputs => {
  const oldInputsRef = useRef(inputs);
  const inputValuesArray = Object.values(inputs);
  const inputKeysArray = Object.keys(inputs);
  useMemo(() => {
    const oldInputs = oldInputsRef.current;

    compareInputs(inputKeysArray, oldInputs, inputs);

    oldInputsRef.current = inputs;
  }, inputValuesArray); // eslint-disable-line react-hooks/exhaustive-deps
};

Это можетзатем использовать его, скопировав литерал массива зависимостей и просто изменив его на литерал объекта:

useDependenciesDebugger({ state1, state2 });

Это позволяет журналированию знать имена переменных без какого-либо отдельного параметра для этой цели.

Edit useDependenciesDebugger

...