React useEffect 후크가 초기 렌더링에서 실행되지 않도록 설정


106

문서에 따르면 :

componentDidUpdate()업데이트가 발생한 직후에 호출됩니다. 이 메서드는 초기 렌더링에는 호출되지 않습니다.

새로운 useEffect()후크를 사용하여 를 시뮬레이션 할 수 componentDidUpdate()있지만 useEffect()처음으로도 렌더링 할 때마다 실행되는 것처럼 보입니다 . 초기 렌더링에서 실행되지 않도록하려면 어떻게합니까?

아래 예에서 볼 수 있듯이 componentDidUpdateFunction는 초기 렌더링 componentDidUpdateClass중에 인쇄 되지만 초기 렌더링 중에 인쇄되지 않았습니다.

function ComponentDidUpdateFunction() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    console.log("componentDidUpdateFunction");
  });

  return (
    <div>
      <p>componentDidUpdateFunction: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

class ComponentDidUpdateClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidUpdate() {
    console.log("componentDidUpdateClass");
  }

  render() {
    return (
      <div>
        <p>componentDidUpdateClass: {this.state.count} times</p>
        <button
          onClick={() => {
            this.setState({ count: this.state.count + 1 });
          }}
        >
          Click Me
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <div>
    <ComponentDidUpdateFunction />
    <ComponentDidUpdateClass />
  </div>,
  document.querySelector("#app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>


1
렌더링 수를 기반으로하고 명시적인 상태 변수가 아닌 작업을 수행하는 것이 합리적 일 때 사용 사례가 무엇인지 물어볼 수 count있습니까?
Aprillion 2018

답변:


122

useRef후크를 사용하여 원하는 변경 가능한 값을 저장할 수 있으므로이 값을 사용하여 useEffect함수가 처음 실행 되는지 추적 할 수 있습니다 .

동일한 단계에서 효과가 실행되도록 componentDidUpdate하려면 useLayoutEffect대신 사용할 수 있습니다 .

const { useState, useRef, useLayoutEffect } = React;

function ComponentDidUpdateFunction() {
  const [count, setCount] = useState(0);

  const firstUpdate = useRef(true);
  useLayoutEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;
      return;
    }

    console.log("componentDidUpdateFunction");
  });

  return (
    <div>
      <p>componentDidUpdateFunction: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

ReactDOM.render(
  <ComponentDidUpdateFunction />,
  document.getElementById("app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>


5
나는 대체하는 시도 useRef와 함께 useState,하지만 세터를 사용하여에 할당 할 때 발생되지 않는, 다시는 렌더링 트리거 firstUpdate.current난이 :) 유일한 좋은 방법입니다 생각 때문에
Aprillion

2
누군가 DOM을 변경하거나 측정하지 않는 경우 레이아웃 효과를 사용하는 이유를 설명 할 수 있습니까?
ZenVentzi

5
@ZenVentzi이 예제에서는 필요하지 않지만 질문은 componentDidUpdate후크 로 모방하는 방법 이었기 때문에 사용했습니다.
Tholle

1
이 답변을 기반으로 여기에 사용자 지정 후크를 만들었습니다 . 구현해 주셔서 감사합니다!
Patrick Roberts

64

다음 과 같이 사용자 정의 후크 로 전환 할 수 있습니다 .

import React, { useEffect, useRef } from 'react';

const useDidMountEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        if (didMount.current) func();
        else didMount.current = true;
    }, deps);
}

export default useDidMountEffect;

사용 예 :

import React, { useState, useEffect } from 'react';

import useDidMountEffect from '../path/to/useDidMountEffect';

const MyComponent = (props) => {    
    const [state, setState] = useState({
        key: false
    });    

    useEffect(() => {
        // you know what is this, don't you?
    }, []);

    useDidMountEffect(() => {
        // react please run me if 'key' changes, but not on initial render
    }, [state.key]);    

    return (
        <div>
             ...
        </div>
    );
}
// ...

2
이 접근 방식은 종속성 목록이 배열 리터럴이 아니라는 경고를 표시합니다.
theprogrammer

1
내 프로젝트에서이 후크를 사용했는데 경고가 표시되지 않았습니다. 더 많은 정보를 제공 할 수 있습니까?
Mehdi Dehghani

1
@vsync 당신은 결코 다시 초기 렌더링에 한 번 효과를 실행하려면 다른 사건에 대해있는 거 생각
가이 프로그래밍

2
@vsync의 노트 섹션에서 reactjs.org/docs/... 은 특히 당신이 효과를 실행하고 (마운트 및 마운트 해제에) 한 번만를 정리하려는 경우, 당신은 빈 배열을 전달할 수 있습니다 "라고 ([])는로 두 번째 인수. " 이것은 나를 위해 관찰 된 행동과 일치합니다.
Programming Guy

10

useFirstRender양식 입력에 초점을 맞추는 것과 같은 경우를 처리하기 위해 간단한 후크를 만들었습니다 .

import { useRef, useEffect } from 'react';

export function useFirstRender() {
  const firstRender = useRef(true);

  useEffect(() => {
    firstRender.current = false;
  }, []);

  return firstRender.current;
}

그것은 밖으로 시작 true후 전환, false에서 useEffect결코 다시 한 번만 실행되는, 및.

구성 요소에서 다음을 사용하십시오.

const firstRender = useFirstRender();
const phoneNumberRef = useRef(null);

useEffect(() => {
  if (firstRender || errors.phoneNumber) {
    phoneNumberRef.current.focus();
  }
}, [firstRender, errors.phoneNumber]);

귀하의 경우에는 if (!firstRender) { ....


3

@ravi, 당신은 전달 된 마운트 해제 기능을 호출하지 않습니다. 다음은 좀 더 완전한 버전입니다.

/**
 * Identical to React.useEffect, except that it never runs on mount. This is
 * the equivalent of the componentDidUpdate lifecycle function.
 *
 * @param {function:function} effect - A useEffect effect.
 * @param {array} [dependencies] - useEffect dependency list.
 */
export const useEffectExceptOnMount = (effect, dependencies) => {
  const mounted = React.useRef(false);
  React.useEffect(() => {
    if (mounted.current) {
      const unmount = effect();
      return () => unmount && unmount();
    } else {
      mounted.current = true;
    }
  }, dependencies);

  // Reset on unmount for the next mount.
  React.useEffect(() => {
    return () => mounted.current = false;
  }, []);
};


안녕하세요 @Whatabrain, 비 의존성 목록을 전달할 때이 사용자 지정 후크를 사용하는 방법은 무엇입니까? componentDidmount하지만 뭔가 같은 동일 할 것이다 빈되지 않음useEffect(() => {...});
KevDing

1
@KevDing dependencies호출 할 때 매개 변수 를 생략하는 것처럼 간단해야 합니다.
Whatabrain

0

@MehdiDehghani, 솔루션이 완벽하게 작동합니다. 한 가지 추가해야 할 추가 사항은 마운트 해제에서 didMount.current값을 false. 이 사용자 지정 후크를 다른 곳에서 사용하려고하면 캐시 값을 얻지 못합니다.

import React, { useEffect, useRef } from 'react';

const useDidMountEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        let unmount;
        if (didMount.current) unmount = func();
        else didMount.current = true;

        return () => {
            didMount.current = false;
            unmount && unmount();
        }
    }, deps);
}

export default useDidMountEffect;

2
구성 요소가 언 마운트되면 재 마운트되면 didMount가 이미으로 다시 초기화되기 때문에 이것이 필요한지 확신 할 수 없습니다 false.
Cameron Yick
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.