강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

front님의 프로필 이미지
front

작성한 질문수

웹 게임을 만들며 배우는 React

6-5. useMemo와 useCallback

useState() 첫 번째 인자 관련

작성

·

235

0

안녕하세요 선생님, 항상 좋은 강의 감사드립니다.

 

로또 추첨기를 복습하면서 함수형 컴포넌트 형태로  만들어보던 중에 아래와 같이 코드를 작성하게 됐습니다.

const Lotto = () => {

const [winNumbers, setWinNumbers] = useState(getWinNumbers);
...

 

useState()의 첫 번째 인자로 getWinNumbers()가 아닌 함수 자체를 넣어줬는데 정상 동작하는 것을 확인했습니다. 또한, useMemo()를 적용한 것처럼 화면이 리렌더링 되더라도 getWinNumber 함수가 재호출되지 않았습니다.

 

리액트 공식문서를 찾아봤지만 함수를 넣어주는 형태는 찾지 못했는데, 혹시 이게 유효한 문법일까요?

 

답변 1

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

저러면 winNumbers가 getWinNumbers 함수 자체가 되어버립니다. 근데 아마도 setWinNumbers가 바로 호출되어 덮어씌워졌을 겁니다.

저렇게 하면 const [winNumbers, setWinNumbers] = useState(); 한 것이나 다름 없습니다.

front님의 프로필 이미지
front
질문자

답변 감사드립니다.

추가적으로 질문이 있습니다.

setWinNumbers가 바로 호출되어서 덮어씌워졌을 것이다 라고 말씀해주셔서 console.log()를 이용해 아래와 같이 로그를 찍어봤습니다.

console.log('%cfirst','background: red');
const [winNumbers, setWinNumbers] = useState(getWinNumbers);
console.log(winNumbers);
console.log('%csecond', 'background: blue');
...

setWinNumbers가 호출된 적이 없지만 winNumber가 정상적인 값을 가지고 있습니다.

 

추가적으로,

const [winNumbers, setWinNumbers] = useState();

와 같이 작성했을 경우에는 에러가 발생했습니다.

 

제가 무언가 놓치고 있다면 알려주시면 감사하겠습니다.

전체적인 소스 코드는 아래와 같습니다.

import React, { useCallback, useEffect, useRef, useState } from 'react';
import * as _ from 'lodash';
import Ball from './Ball';

const getWinNumbers = () => {
console.log('getWinNumbers() 함수가 호출되었습니다.');
const sevenNumbers = _.chain(_.range(1, 46))
.shuffle()
.take(7)
.value();

const bonusNumber = sevenNumbers[sevenNumbers.length - 1];
const sortedLottoNumbers = sevenNumbers.slice(0, 6).sort((a, b) => a - b);

return [...sortedLottoNumbers, bonusNumber];
}

const Lotto = () => {

console.log('%cfirst','background: red');
const [winNumbers, setWinNumbers] = useState(getWinNumbers);
console.log(winNumbers);
console.log('%csecond', 'background: blue');
const [winBalls, setWinBalls] = useState([]);
const [bonus, setBonus] = useState(null);
const [redo, setRedo] = useState(false);
const timeouts = useRef([]);

useEffect(() => {
console.log('%ctimeouts useEffect 시작', 'background: green');
_.chain(_.range(6))
.map(i => setTimeout(() => {
setWinBalls(prevWinBalls => [...prevWinBalls, winNumbers[i]]);
}, 1000 * (i + 1)))
.each(timeout => timeouts.current.push(timeout))
.value();

timeouts.current.push(setTimeout(() => {
setRedo(true);
setBonus(winNumbers[winNumbers.length - 1]);
}, 7000));

return () => {
console.log('%ctimeouts useEffect 리턴', 'background: green');
timeouts.current.forEach(clearTimeout);
}
}, [timeouts.current])

const onClickRedo = useCallback(() => {
console.log('redo 이벤트 발생!');
setWinNumbers(getWinNumbers());
setWinBalls([]);
setBonus(null);
setRedo(false);
timeouts.current = [];
}, [winNumbers]);

return (
<>
<div>
<div>당첨 숫자</div>
{winBalls.map(num => <Ball number={num} key={num}></Ball>)}
</div>

<div>보너스</div>
{bonus && <Ball number={bonus}></Ball>}
{redo && <button onClick={onClickRedo}>한 번 더!</button>}
</>
);
};

export default Lotto;

 

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

아, 제가 착각했습니다. 아래 기능이라서 useState에 함수를 넣으면 그 함수가 실행됩니다. 보통은 비동기 작업을 한 후에 state를 만들 때 실행됩니다.

https://reactjs.org/docs/hooks-reference.html#lazy-initial-state

추가로 getWinNumbers를 useMemo로 감쌀 필요도 없어집니다. 처음 한 번만 실행돼서요.

front님의 프로필 이미지
front
질문자

많은 도움이 됐습니다. 답변 감사드립니다~

front님의 프로필 이미지
front

작성한 질문수

질문하기