inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

웹 게임을 만들며 배우는 React

6-4. useEffect로 업데이트 감지하기

이번강의 코드의 동작결과를 바탕으로 예측한 동작순서가 맞는지 알고싶습니다.

해결된 질문

242

띄융

작성한 질문수 4

0

이번 강의를 보고 코드를 정리한 후 실행결과를 확인했습니다.

콘솔이 생각했던것 보다 7라인 더 찍혀서 궁금해서 질문 남깁니다. 물론 다음 강의에 useMemo나 useCallback으로 문제를 해결하겠지만, 제가 생각한 동작 순서가 맞는지 궁금합니다.

 

코드

import React, { useEffect, useRef, useState, useMemo } from 'react'
import Ball from './Ball'

function getWinNumbers() {
  console.log('getWinNumbers함수')
  const candidate = Array(45)
    .fill()
    .map((v, i) => i + 1)
  const shuffle = []
  while (candidate.length > 0) {
    shuffle.push(
      candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]
    )
  }
  const bonusNumber = shuffle[shuffle.length - 1]
  const winNumbers = shuffle.slice(0, 6).sort((p, c) => p - c)
  return [...winNumbers, bonusNumber]
}

const Lotto = () => {
  const [winNumbers, setWinNumbers] = useState(getWinNumbers())
  const [winBalls, setWinBalls] = useState([])
  const [bonus, setBonus] = useState(null)
  const [redo, setRedo] = useState(false)
  const timeouts = useRef([])

  useEffect(() => {
    console.log('useEffect1')
    for (let i = 0; i < winNumbers.length - 1; i++) {
      timeouts.current[i] = setTimeout(() => {
        setWinBalls(prevState => [...prevState, winNumbers[i]])
      }, (i + 1) * 1000)
    }
    timeouts.current[6] = setTimeout(() => {
      setBonus(winNumbers[6])
      setRedo(true)
    }, 7000)
    return () => {
      timeouts.current.forEach(v => {
        clearTimeout(v)
      })
    }
  }, [timeouts.current]) // 빈 배열이면 componentDidMount와 동일
  // 배열에 요소가 있으면 componentDidMount랑 componentDidUpdate 둘 다 수행

  useEffect(() => {
    console.log('useEffect2 - 로또 숫자를 생성합니다.')
  }, [winNumbers])

  const onClickRedo = () => {
    console.log('onClickRedo')
    setWinNumbers(getWinNumbers())
    setWinBalls([])
    setBonus(null)
    setRedo(false)
    timeouts.current = []
  }

  return (
    <>
      <div>Win Numbers</div>
      <div id="결과창">
        {winBalls.map(v => (
          <Ball key={v} number={v} />
        ))}
      </div>
      <div>Bonus!</div>
      {bonus && <Ball number={bonus} />}
      {redo && <button onClick={onClickRedo}>One more</button>}
    </>
  )
}

export default Lotto

 

동작 결과 콘솔

11:46:54.502 Lotto.jsx:5 getWinNumbers함수

11:46:54.532 Lotto.jsx:28 useEffect1

11:46:54.532 Lotto.jsx:47 useEffect2 - 로또 숫자를 생성합니다.

11:46:55.951 Lotto.jsx:5 getWinNumbers함수

11:46:56.942 Lotto.jsx:5 getWinNumbers함수

11:46:57.729 Lotto.jsx:5 getWinNumbers함수

11:46:58.536 Lotto.jsx:5 getWinNumbers함수

11:46:59.535 Lotto.jsx:5 getWinNumbers함수

11:47:00.545 Lotto.jsx:5 getWinNumbers함수

11:47:01.536 Lotto.jsx:5 getWinNumbers함수

 

예측한 동작 순서

1. 함수 컴포넌트에 있는 winNumbers가 getWinNumbers 함수 호출하면서 getWinNumbers함수속 console 출력 -> 'getWinNumbers함수'

2. 27번줄의 useEffect의 두번째 인자인 timeouts.current가 [] 이기 때문에 첫번쨰 useEffect의 console 출력 -> 'useEffect1'

3. 46번줄의 useEffect의 두번째 인자인 winNumbers가 7개의 배열요소가 들어있기 때문에 true 이다 그래서 두번째 useEffect의 console 출력 -> 'useEffect2 - 로또 숫자를 생성합니다.'

4. useEffect1이 출력될때 아래 코드가 6번 동작해서 getWinNumbers함수 속에있는 console  출력 -> 'getWinNumbers함수' x 6

 for (let i = 0; i < winNumbers.length - 1; i++) {

      timeouts.current[i] = setTimeout(() => {

        setWinBalls(prevState => [...prevState, winNumbers[i]]) 

      }, (i + 1) * 1000)

    }

 

그리고 아래 코드가 한번 동작해서  getWinNumbers함수 속에있는 console  출력 -> 'getWinNumbers함수'

    timeouts.current[6] = setTimeout(() => {

      setBonus(winNumbers[6])

      setRedo(true)

    }, 7000)

 

동작 순서 4번에서 (setWinBalls)와 (setBonus + setRedo)가 비동기함수 라서 앞선 콘솔들이 찍히고 마지막에 찍혔던 거죠?

 

 

 

react

답변 1

1

제로초(조현영)

네 맞습니다. 참고로 useEffect는 deps와 상관없이 무조건 처음 한 번은 실행됩니다.

0

띄융

네네 다음강의를 보고 나니 useEffect 가 class 의 componentDidMount 역할을 유사하게 하는것처럼 무조건 한번은 실행된다는걸 이해했습니다!

 

제가 생각한 코드 순서가 맞다니까 다행이네요. 강의 이해가 너무 잘됩니다. 감사합니다~!

npm run dev 실행 시 포트가 안뜨는 문제

0

240

2

timeouts.current를 useEffect 의 input값으로 넣었을때

0

98

2

렌더링 테스트 코드 (Hooks)

0

93

1

Cannot find package 'react-refesh' 이런 에러 뜨시는 분들 보세요.

0

167

1

해당 에러 뜨는 분들 보세요. "Uncaught TypeError: ReactDom.createRoot is not a function"

1

209

1

강사님 레포지토리에서 코드 복사 시 master 브랜치 말고 react18 브랜치꺼 복붙하세요ㅠㅠ

0

108

1

useMemo와 useCallback 사용 시기

0

221

2

onRightClickTd가 작동을 하지 않습니다.

0

237

1

action.type 불러오는 방식

0

226

2

onClickRedo 질문

0

177

1

const Try = require(./Try) 빨간줄

0

262

1

npx webpack 실행시

0

324

1

지뢰찾기 강좌에서 빈칸들 한번에 열기 파트에서 여쭤보고싶은부분이 있어서 글 올립니다.

0

243

1

강좌에서 다루지 않은 기능들은 어떻게 학습하면 좋을까요?

0

319

1

react devtool이 enable 않됩니다.

0

548

2

React 랜더링이 되지 않습니다.

0

422

2

비동기로 동작하는 setState에 대해서

0

340

1

npm run dev 할 때 에러발생

0

497

2

memo, PureComponenet, shouldComponentUpdate 관련 질문

0

211

1

devMiddleware의 필요성

0

358

1

리액트에서 화살표 함수를 사용해야하는 이유

0

948

2

path.join관련질문

0

288

2

2-9. 웹팩 데브 서버와 핫 리로딩 설치과정 시 에러

0

384

1

next.js 에서 이와 비슷한 예제를 돌리고있는데 react랑 달라서 질문 드립니

1

503

4