• 카테고리

    질문 & 답변
  • 세부 분야

    프론트엔드

  • 해결 여부

    미해결

로또 질문있습니다.

22.03.20 18:55 작성 조회수 141

0

안녕하세요 제로초님!


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);
  console.log("winNumbers1:", winNumbers);

  return [...winNumbers, bonusNumber];
}

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

  console.log("winNumbers2:", winNumbers);
  console.log("winBalls:", winBalls);

  useEffect(() => {
    console.log("useEffect");
    for (let i = 0; i < winNumbers.length - 1; i++) {
      timeouts.current[i] = setTimeout(() => {
        setWinBalls((prevBalls) => [...prevBalls, 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("로또 숫자를 생성합니다.");
  }, [winNumbers]);

  const onClickRedo = useCallback(() => {
    console.log("onClickRedo");
    console.log(winNumbers);
    setWinNumbers(getWinNumbers());
    setWinBalls([]);
    setBonus(null);
    setRedo(false);
    timeouts.current = [];
  }, [winNumbers]);

강좌와 같은 코드에서 getwinNumbers 함수안에서 winNumbers의 값을(winNumbers1) 콘솔로 한번 찍고

 
function getWinNumbers() {
......
  console.log("winNumbers1:", winNumbers);

  return [...winNumbers, bonusNumber];
}

이번에는 Memo 안에서 winNumbers를 (winNumbers2)콘솔로 찍어보고

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

  console.log("winNumbers2:", winNumbers);
  console.log("winBalls:", winBalls);

브라우저를 실행하면

이와같이 winNumbebrs1과 2가가 처음에는 두개의 콘솔 모두 같은 값이었다가 useEffect가 실행되고 나서 값이 바뀝니다.

이런 현상은 초기 렌더링이거나 새로고침을 하면 똑같이 일어납니다.

하지만 onClickRedo한번더를 누르면 그제서야 winNumbers의 값이 같게 나옵니다. 

왜 처음렌더링하거나 새로고침시 값이 다르게 나오는지 궁급하니다!.

 

2.  

강의 5-2 에서

 

componenetDidMount () {

const {imageCord} = this.state

this.interval = setInterval( () => {

console.log("hello", this state.imageCord, rspCoords,가위)

if (imageCord === repCoords.바위)

.......

}

}

이코드에서 비동기 함수 바깥에 있는 변수 imgeCord를 참조해서 클로져 에러가 발생한다고 하셨잖아요?

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


  useEffect(() => {
    console.log("useEffect");
    for (let i = 0; i < winNumbers.length - 1; i++) {
      timeouts.current[i] = setTimeout(() => {
        setWinBalls((prevBalls) => [...prevBalls, winNumbers[i]]);
      }, (i + 1) * 1000);
  }

그런데 이코드에서 setTimeout 비동기 함수에서 winNumbers와 timeouts도 useState에 있는 변수?(상수)인데 이때는 왜 클로져 문제가 발생하지 않는지 궁금합니다!

 

 

 

답변 1

답변을 작성해보세요.

0

1.저는 이 문제가 발생하지 않는데 저와 코드가 다른 것 같습니다.

2. let i와 setTimeout의 콜백함수 간에 클로저 관계가 생성되어서 해결되었기 때문입니다. 즉, 이 방법이 클로저 문제의 모범답안입니다.