programing

초기 렌더링 시 리액트 useEffect 후크를 실행하지 않도록 설정

prostudy 2022. 9. 12. 10:36
반응형

초기 렌더링 시 리액트 useEffect 후크를 실행하지 않도록 설정

문서에 따르면:

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

것을 사용할 수 .useEffect()하여 ""를 시뮬레이트합니다.componentDidUpdate() 그것은 , 지, ,것, , , , , 。useEffect()츠키다이치노초기 렌더링 시 실행되지 않도록 하려면 어떻게 해야 합니까?

볼수, 음음음 as as as as as as as as as as as as as as as as as as as as 。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>

훅을 사용하여 원하는 임의의 가변값을 저장할 수 있습니다. 따라서 이 값을 사용하여 처음 이 을 추적하는 데 사용할 수 있습니다.useEffect기능을 실행하고 있습니다.

를 「」와 하고 싶은 .componentDidUpdate네, 대신 사용할 수 있습니다.

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>

다음과 같이 커스텀 훅으로 전환할 수 있습니다.

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>
    );
}
// ...

나는 심플하게 만들었다.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]);

같은 신, 신, 신, 음, 음, 음, 음, 음, 음, 음, for, for, for, for, for, for, for을 사용합니다.if (!firstRender) { ....

Tholle의 답변과 같은 접근법이지만useStateuseRef.

const [skipCount, setSkipCount] = useState(true);

...

useEffect(() => {
    if (skipCount) setSkipCount(false);
    if (!skipCount) runYourFunction();
}, [dependencies])

편집

이것도 동작하지만 컴포넌트가 재렌더되는 원인이 되는 상태 갱신을 수반합니다. 컴포넌트가 ★★★★★★★★★★★★★★★★★★★★★★★★★」useEffect콜(및 그 자녀 모두)에 의존 배열을 가지고 있습니다.이것은 문제가 되지 않습니다., 어떤 것이든 해 주십시오.useEffect 배열 (Dependency ArrayuseEffect(() => {...})시시시실실실다다

" " " " 」의 및 useRef재검토의 원인이 되지 않습니다.

@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;
  }, []);
};

것 중 입니다.typescriptRef는 또한 useEffect컴포넌트 마운트 해제 시 정리를 수행합니다.

import {
  useRef,
  EffectCallback,
  DependencyList,
  useEffect
} from 'react';

/**
 * @param effect 
 * @param dependencies
 *  
 */
export default function useNoInitialEffect(
  effect: EffectCallback,
  dependencies?: DependencyList
) {
  //Preserving the true by default as initial render cycle
  const initialRender = useRef(true);

  useEffect(() => {
    let effectReturns: void | (() => void) = () => {};

    // Updating the ref to false on the first render, causing
    // subsequent render to execute the effect
    if (initialRender.current) {
      initialRender.current = false;
    } else {
      effectReturns = effect();
    }

    // Preserving and allowing the Destructor returned by the effect
    // to execute on component unmount and perform cleanup if
    // required.
    if (effectReturns && typeof effectReturns === 'function') {
      return effectReturns;
    } 
    return undefined;
  }, dependencies);
}

쓸 수요, 그냥 쓸 수요.useEffect훅을 설정했지만, 이번에는 초기 렌더링에서는 실행되지 않습니다.이 갈고리의 사용법은 다음과 같습니다.

useuseNoInitialEffect(() => {
  // perform something, returning callback is supported
}, [a, b]);

ESLint를 사용하여 이 커스텀훅에 react-hooks/exhaustive-deps 규칙을 사용하는 경우:

{
  "rules": {
    // ...
    "react-hooks/exhaustive-deps": ["warn", {
      "additionalHooks": "useNoInitialEffect"
    }]
  }
}

은 '만들다'를 만드는 것입니다.let사실대로 말하다

그런 다음 true가 false로 설정되어 있으면 useEffect 함수를 반환(정지)합니다.

다음과 같이 합니다.


    import { useEffect} from 'react';
    //your let must be out of component to avoid re-evaluation 
    
    let isFirst = true
    
    function App() {
      useEffect(() => {
          if(isFirst){
            isFirst = false
            return
          }
    
        //your code that don't want to execute at first time
      },[])
      return (
        <div>
            <p>its simple huh...</p>
        </div>
      );
    }

@Carmine Tambasciabs 솔루션과 유사하지만 상태를 사용하지 않습니다. : )

function useEffectAfterMount(effect, deps) {
  const isMounted = useRef(false);

  useEffect(() => {
    if (isMounted.current) return effect();
    else isMounted.current = true;
  }, deps);

  // reset on unmount; in React 18, components can mount again
  useEffect(() => {
    isMounted.current = false;
  });
}

우리가 돌려받은 걸 돌려줘야 해effect()정리 함수일 수 있습니다.하지만 우리는 그것이 사실인지 아닌지를 결정할 필요가 없다. 해 주세요.useEffect알아내세요.

)이라고.라고했했 했했했다다isMounted.current = false는 필요 없었습니다.그러나 React 18에서는 컴포넌트가 이전 상태로 재마운트할 수 있기 때문입니다(@Whatabrain @).

@합니다.마운트 시 한 .@MehdiDehgani를 마운트 해제 시 한 가지 추가 작업을 수행해야 합니다.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;

심플한 도입

import { useRef, useEffect } from 'react';

function MyComp(props) {

  const firstRender = useRef(true);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
    } else {
      myProp = 'some val';
    };

  }, [props.myProp])


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

}

하는 것은 했기 때문에.useLayoutEffect 제 풀 변수의 값이 되어 있지 입니다.selectedItem 합니다.useEffect콜백은 초기 렌더링 여부를 판단하기 위한 원래 값입니다.

export default function MyComponent(props) {
    const [selectedItem, setSelectedItem] = useState(null);

    useEffect(() => {
        if(!selectedItem) return; // If selected item is its initial value (null), don't continue
        
        //... This will not happen on initial render

    }, [selectedItem]);

    // ...

}

첫 번째 렌더를 건너뛸 경우 "firstRenderDone" 상태를 만들고 useEffect with empty dependency list(didMount와 동일하게 동작함)에서 true로 설정할 수 있습니다.그런 다음 다른 useEffect에서 작업을 수행하기 전에 첫 번째 렌더가 이미 완료되었는지 확인할 수 있습니다.

const [firstRenderDone, setFirstRenderDone] = useState(false);

//useEffect with empty dependecy list (that works like a componentDidMount)
useEffect(() => {
  setFirstRenderDone(true);
}, []);

// your other useEffect (that works as componetDidUpdate)
useEffect(() => {
  if(firstRenderDone){
    console.log("componentDidUpdateFunction");
  }
}, [firstRenderDone]);

위의 모든 것은 양호하지만 기본적으로 처음 실행되지 않고 종속성이 있는 if 조건(또는 기타)을 사용하여 useEffect의 액션을 "건너뛰기"할 수 있다는 점을 고려하면 이는 보다 간단한 방법으로 달성할 수 있습니다.

예를 들어 다음과 같은 경우가 있습니다.

  1. API에서 데이터를 로드하지만 내 제목은 날짜가 없을 때까지 "Loading"이어야 합니다. 따라서 배열과 처음에 비어 있는 둘러보기와 "Showing" 텍스트가 표시됩니다.
  2. 이러한 API와 다른 정보를 사용하여 컴포넌트를 렌더링합니다.
  3. 사용자는 이러한 정보를 하나씩 삭제할 수 있으며, 처음부터 투어 어레이를 다시 비워둘 수도 있지만, 이번에는 API 가져오기가 이미 완료되어 있습니다.
  4. 투어 목록을 삭제하여 비워두면 다른 제목을 표시합니다.

따라서 "솔루션"은 다른 useState를 생성하여 데이터 가져오기 후에만 변경되는 부울값을 생성하여 useEffect의 다른 조건을 true로 만들고 투어 길이에 따라 다른 함수를 실행하는 것이었습니다.

useEffect(() => {
  if (isTitle) {
    changeTitle(newTitle)
  }else{
    isSetTitle(true)
  }
}, [tours])

여기 내 앱.js

import React, { useState, useEffect } from 'react'
import Loading from './Loading'
import Tours from './Tours'

const url = 'API url'

let newTours

function App() {
  const [loading, setLoading ] = useState(true)
  const [tours, setTours] = useState([])
  const [isTitle, isSetTitle] = useState(false)
  const [title, setTitle] = useState("Our Tours")

  const newTitle = "Tours are empty"

  const removeTours = (id) => {
    newTours = tours.filter(tour => ( tour.id !== id))

    return setTours(newTours)
  }

  const changeTitle = (title) =>{
    if(tours.length === 0 && loading === false){
      setTitle(title)
    }
  }

const fetchTours = async () => {
  setLoading(true)

  try {
    const response = await fetch(url)
    const tours = await response.json()
    setLoading(false)
    setTours(tours)
  }catch(error) {
    setLoading(false)
    console.log(error)
  }  
}


useEffect(()=>{
  fetchTours()
},[])

useEffect(() => {
  if (isTitle) {
    changeTitle(newTitle)
  }else{
    isSetTitle(true)
  }
}, [tours])


if(loading){
  return (
    <main>
      <Loading />
    </main>
  )  
}else{
  return ( 

    <main>
      <Tours tours={tours} title={title} changeTitle={changeTitle}           
removeTours={removeTours} />
    </main>
  )  
 }
}



export default App

언급URL : https://stackoverflow.com/questions/53253940/make-react-useeffect-hook-not-run-on-initial-render

반응형