본문 바로가기

Client/React.js

<리액트 기초> useEffect

 

* useEffect

 - 지난 장에서도 봤듯이 useEffect는 Hooks에서 componentDidMount, componentDidUpdate, componentWillUnmount의 역할을 한다.

 

 - 이번 장과 다음 장에서 다룰 예시는 함수로 랜덤한 숫자 6개를 배열에 담고, 그 숫자들을 1초간격으로 하나씩 보여주는 것이다. 추가로 버튼을 누르면 새로운 숫자 6개로 다시 시작한다.

 

function getWinNumbers() {
//   winNumbers라는 배열에 숫자 6개를 넣어서 반환하는 함수
}

const Test0 = () => {
  const [winNumbers, setWinNumbers] = useState(getWinNumbers());
  const [winBalls, setWinBalls] = useState([]);
  const [redo, setRedo] = useState(false);
  const timeouts = useRef([]);
  
  //...

}

 

 - state를 쓰지 않는 함수는 컴포넌트 외부에서 선언하는 것이 좋으므로 winNumbers라는 배열에 숫자 6개를 담는 함수를 외부에 선언하였다. 그것을 state로 갖도록 컴포넌트에서 담아주었다.

 

 - 숫자 6개를 1초 간격으로 추가로 보여주기 위해서 winBalls를 배열로 만들어서 하나씩 넣어줄 것이다. redo 는 숫자 6개가 다 만들어지고 버튼이 생길 수 있도록 false값을 주었다.

 

 - setTimeout을 써야 하므로 useRef를 선언하였다. 앞서 잠깐 다뤘었는데 배열에 담긴 숫자 각각 1초, 2초, 3초라는 다른 시간을 넣어줘야하므로 배열에 담아서 시간을 설정할 것이다.

 

 - 이제 useEffect를 설정하자.

 

  useEffect( () => {
    for (let i = 0; i < winNumbers.length; i++) {
      timeouts.current[i] = setTimeout(() => {
        setWinBalls((prevBalls) => [...prevBalls, winNumbers[i]]);
      }, (i + 1) * 1000);
    }
    setRedo(true);
    return () => {
      timeouts.current.forEach( (v) => {
        clearTimeout(v)
      })
    }
  }, [timeouts.current]); 

 

 - useEffect는 기본적으로 useEffect( ( ) => { }, [ ] ); 형태를 갖는다.

 

 - 여기서 두 번째 인자인 배열에 요소를 넣어서 사용하는데, 만약 빈 배열을 넣는다면 componentDidMount와 동일하게 작동한다. 즉 처음 렌더링 되었을 때 한번 실행되고 다시는 실행되지 않는다.

 

 - 배열에 요소가 있으면 componentDidMount와 componentDidUpdate 가 둘 다 실행 되는 것과 같다. 넣은 요소가 변할 때마다 다시 실행된다.

 

 - return 부분에는 반드시 Unmount에 넣을 것을 넣어야한다. 여기서는 배열 원소 각각의 time을 클리어하도록 forEach를 사용하였다.

 

 - 이 경우에는 DidUpdate가 더 적절한데, 버튼이 눌릴 때마다 다시 실행되어야 하기 때문이다. 따라서 두 번째 인자로 timeouts를 넣으면 된다. 여기에 조건을 적절하게 넣어야 DidMount와 함께 두 번 실행되는 것을 막을 수 있다.

 

 - DidMount는 무조건 실행된다고 생각해야한다. 그러나 위처럼 DidUpdate에서만 특정 블럭을 사용하고 싶다면 조건을 유의해서 넣는 방식외에 임의의 변수를 선언해서 활용하는 방법이 있다.

 

const mounted = useRef(false);

useEffect( () => {
  if(!mounted.current) {
    mounted.current = true;
  } else {
    //...
  }
}, [바뀌는값] )

 

 - 위와 같이 처음에 임의의 변수에 false를 담고 if문을 활용해서 처음 실행될때에는 이 값을 true로 바뀌게 하여 그 뒤로는 else가 실행되도록한다. 이렇게 하면 DidUpdate의 블럭만 실행되도록 할 수 있다.

 

  return (
    <>
      <div>당첨 숫자</div>
      <div id="결과창">
        {winBalls.map( (v) => <Ball key={v} number={v} />)}
      </div>
      {redo && <button onClick={onClickRedo}>한 번 더!</button>}
    </>
  )

 

 - 렌더 부분은 위와 같다. winBalls에 숫자를 하나씩 담고 이를 map메서드로 스타일되어있는 태그로 반환한다.

 

 - 클릭함수부분과 위의 컴포넌트를 개선하는 것은 다음 장 useMemo와 useCallback에서 다뤄보자.

 

 


 

 

참고

 

 

 이 글은 ZeroCho 님의 리액트 무료 강좌를 수강하며 개인적으로 정리하며 쓰는 글입니다.

 

 

 

 

인프런

 

웹 게임을 만들며 배우는 React - 인프런

웹게임을 통해 리액트를 배워봅니다. Class, Hooks를 모두 익히며, Context API와 React Router, 웹팩, 바벨까지 추가로 배웁니다. 초급 웹 개발 프레임워크 및 라이브러리 React 웹 개발 게임개발 온라인 강

www.inflearn.com

 

유튜브

 

리액트 무료 강좌(웹게임)

 

www.youtube.com