programing

useEffect 훅을 발생시킨 의존관계 배열 변수 확인

starjava 2023. 4. 2. 09:40
반응형

useEffect 훅을 발생시킨 의존관계 배열 변수 확인

어떤 변수인지 쉽게 판별할 수 있는 방법이 있을까요?useEffect의 의존관계 배열이 함수 재기동을 트리거합니까?

각 변수를 로그아웃하는 것만으로 오해의 소지가 있습니다.a함수와b는, 로그에 기록했을 때에 같은 것으로 표시되는 오브젝트입니다만, 실제로는 다른 오브젝트로, useEffect의 발화를 일으킵니다.

예를 들어 다음과 같습니다.

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

현재의 메서드에서는 과도한 useEffect 콜의 원인이 되는 동작을 인식할 때까지 의존관계 변수를 하나씩 삭제하고 있습니다만, 이 범위를 좁힐 수 있는 더 좋은 방법이 있을 것입니다.

저는 여러 가지 대답에서 조금씩 떼어내서 제 갈고리를 만들었습니다.그냥 떨어뜨릴 수 있는 능력을 원했어요useEffect트리거된 의존관계를 빠르게 디버깅할 수 있습니다.useEffect.

const usePrevious = (value, initialValue) => {
  const ref = useRef(initialValue);
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};
const useEffectDebugger = (effectHook, dependencies, dependencyNames = []) => {
  const previousDeps = usePrevious(dependencies, []);

  const changedDeps = dependencies.reduce((accum, dependency, index) => {
    if (dependency !== previousDeps[index]) {
      const keyName = dependencyNames[index] || index;
      return {
        ...accum,
        [keyName]: {
          before: previousDeps[index],
          after: dependency
        }
      };
    }

    return accum;
  }, {});

  if (Object.keys(changedDeps).length) {
    console.log('[use-effect-debugger] ', changedDeps);
  }

  useEffect(effectHook, dependencies);
};

다음은 두 가지 예입니다.각각의 예에 대해서, 저는 다음과 같이 생각합니다.dep2'foo'에서 'bar'로 바뀝니다.예 1은 통과하지 않은 출력을 나타내고 있습니다.dependencyNames및 예 2는 의 예를 나타내고 있습니다. dependencyNames.

예 1

이전:

useEffect(() => {
  // useEffect code here... 
}, [dep1, dep2])

그 후:

useEffectDebugger(() => {
  // useEffect code here... 
}, [dep1, dep2])

콘솔 출력:

{
  1: {
    before: 'foo',
    after: 'bar'
  }
}

개체 키 '1'은 변경된 종속성의 인덱스를 나타냅니다.여기서,dep2종속성의 두 번째 항목(인덱스 1)이기 때문에 변경됩니다.

예 2

이전:

useEffect(() => {
  // useEffect code here... 
}, [dep1, dep2])

그 후:

useEffectDebugger(() => {
  // useEffect code here... 
}, [dep1, dep2], ['dep1', 'dep2'])

콘솔 출력:

{
  dep2: {
    before: 'foo',
    after: 'bar'
  }
}

@simbathesailor/use-what-changed 마법처럼 잘 작동한다!

  1. 인스톨npm/yarn그리고.--dev또는--no-save

  2. 가져오기 추가:

    import { useWhatChanged } from '@simbathesailor/use-what-changed';
    
  3. 전화:

    // (guarantee useEffect deps are in sync with useWhatChanged)
    let deps = [a, b, c, d]
    
    useWhatChanged(deps, 'a, b, c, d');
    useEffect(() => {
      // your effect
    }, deps);
    

콘솔에 다음과 같은 양호한 차트를 만듭니다.

github에서 로드된 이미지

두 가지 일반적인 원인이 있습니다.

  1. 다음과 같이 전달되는 일부 개체:
// Being used like:
export function App() {
  return <MyComponent fetchOptions={{
    urlThing: '/foo',
    headerThing: 'FOO-BAR'
  })
}
export const MyComponent = ({fetchOptions}) => {
  const [someData, setSomeData] = useState()
  useEffect(() => {
    window.fetch(fetchOptions).then((data) => {
      setSomeData(data)
    })

  }, [fetchOptions])

  return <div>hello {someData.firstName}</div>
}

오브젝트 케이스의 수정은 가능한 경우 컴포넌트 렌더 외부에 있는 스태틱오브젝트를 분할합니다.

const fetchSomeDataOptions = {
  urlThing: '/foo',
  headerThing: 'FOO-BAR'
}
export function App() {
  return <MyComponent fetchOptions={fetchSomeDataOptions} />
}

useMemo로 랩할 수도 있습니다.

export function App() {
  return <MyComponent fetchOptions={
    useMemo(
      () => {
        return {
          urlThing: '/foo',
          headerThing: 'FOO-BAR',
          variableThing: hash(someTimestamp)
        }
      },
      [hash, someTimestamp]
    )
  } />
}

기능에도 동일한 개념이 적용되지만 오래된 폐쇄가 발생할 수 있습니다.

갱신하다

실제로 사용해 본 결과, 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 });

이를 통해 로깅은 별도의 매개 변수 없이 변수 이름을 인식할 수 있습니다.

useDependenciesDebugger 편집

제가 아는 바로는 이 작업을 쉽게 수행할 수 있는 방법은 없지만, 종속성과 변경된 로그를 추적하는 커스텀 훅에 드롭할 수 있습니다.

// 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"

useRef를 사용하여 이전 값을 볼 수 있음을 나타내는 다른 스택오버플로 스레드가 있습니다.

https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state

React 베타 문서에서는 다음 단계를 권장합니다.

  • console.log를 사용하여 종속 어레이를 기록합니다.
  const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
  console.log([todos, tab]);
  • 콘솔에서 다른 리렌더 어레이를 마우스 오른쪽 버튼으로 클릭하고 두 어레이 모두에 대해 "글로벌 변수로 저장"을 선택합니다.만약 당신이 엄격한 모드라면 두 개의 연속적인 것을 비교하지 않는 것이 중요할 수 있습니다.
  • 각 종속성을 비교합니다.
  Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays?

이 질문에는 몇 가지 좋은 답변이 있었습니다만, DX가 마음에 들지 않았습니다.

그래서 저는 가장 사용하기 쉬운 방식으로 변경된 종속성을 기록하는 라이브러리를 작성했고, 두 객체 간의 상세한 비교를 기록하는 기능을 추가했습니다.그러면 객체 내에서 정확히 무엇이 변경되었는지 알 수 있습니다.

나는 그것을 '리액션-무엇이 변화했는가'라고 불렀다.

Readme에는 필요한 예가 모두 포함되어 있습니다.

용도는 매우 간단합니다.

npm install react-what-changed --save-dev
import { reactWhatChanged as RWC } from 'react-what-changed';

function MyComponent(props) {
  useEffect(() => {
    someLogic();
  }, RWC([somePrimitive, someArray, someObject]));
}

이 패키지에는 오브젝트간의 상세한 비교(디프만)를 인쇄하기 위한 2개의 편리한 기능도 있습니다.예를 들어 다음과 같습니다.

import { reactWhatDiff as RWD } from 'react-what-changed';

function MyComponent(props) {
  useEffect(() => {
    someLogic();
  }, [somePrimitive, someArray, someObject]);

  RWD(someArray);
}

언급URL : https://stackoverflow.com/questions/55187563/determine-which-dependency-array-variable-caused-useeffect-hook-to-fire

반응형