• 카테고리

    질문 & 답변
  • 세부 분야

    프론트엔드

  • 해결 여부

    미해결

useState() 첫 번째 인자 관련

21.10.04 15:09 작성 조회수 159

0

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

 

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

const Lotto = () => {

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

 

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

 

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

 

답변 1

답변을 작성해보세요.

0

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

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

front님의 프로필

front

질문자

2021.10.04

답변 감사드립니다.

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

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

질문자

2021.10.06

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