티스토리 뷰

// class
// Lotto.jsx
import React, { Component } 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];
}

class Lotto extends Component {
  state = {
    winNumbers: getWinNumbers(), // 당첨 숫자들
    winBalls: [],
    bonus: null, // 보너스 공
    redo: false,
  };

  timeouts = [];

  runTimeouts = () => {
    const { winNumbers } = this.state;
    for (let i = 0; i < winNumbers.length - 1; i++) {
      this.timeouts[i] = setTimeout(() => {
        this.setState((prevState) => {
          return {
            winBalls: [...prevState.winBalls, winNumbers[i]]
          }
        });
      }, (i + 1) * 1000);
    }
    this.timeouts[6] = setTimeout(() => {
      this.setState({
        bonus: winNumbers[6],
        redo: true, // true 값이 되어서 한번 더 버튼이 보이게 됨
      });
    }, 7000);
  }

  componentDidMount() {
    this.runTimeouts();
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.state.winBalls.length === 0) { // 조건문이 중요! 이에 따라 실행되기 때문에, hooks랑 다를 수 있음
      this.runTimeouts();
    }
  }

  componentWillUnmount() {
    this.timeouts.forEach((v) => {
      clearTimeout(v);
    })
  }

  onClickRedo = () => { // 다시 시작은 초기화
    this.setState({
      winNumbers: getWinNumbers(),
      winBalls: [],
      bonus: null,
      redo: false,
    })
    this.timeouts = []; // 타임아웃도 초기화
  };

  render() {
    const { winBalls, bonus, redo } = this.state;
    return (
      <>
        <div>당첨숫자</div>
        <div id="결과창">{winBalls.map((v) => <Ball key={v} number={v} />)}</div>
        <div>보너스</div>
        {bonus && <Ball number={bonus} />}
        {redo && <button onClick={this.onClickRedo}>한 번 더!</button>}
      </>
    );
  }
}

export default Lotto;
// ball.jsx
import React, { PureComponent } from 'react';

class Ball extends PureComponent {
  render() {
    const {number} = this.props;
    let background;
    if (number <= 10) {
      background = 'red';
    } else if (number <= 20) {
      background = 'orange';
    } else if (number <= 30) {
      background = 'yellow';
    } else if (number <= 40) {
      background = 'blue';
    } else {
      background = 'green';
    }
    return (
      <div className="ball" style={{ background }}>{number}</div>

    );
  }
}

export default Ball;

 

useEffect, useMemo, useCallback

  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);
      });
    }
  }, []); // componentDidMount를 수행
  
  ----------------------------------------------------------------------------------
  
  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와 componentDidUpdate를 모두 수행

useEffect, useMemo, useCallback은 두번째 인자를 가짐

두번째 인자를 가지지 않으면 다시 실행되지 않음

 

[] 두번째 인자가 매우 중요!!! 언제 다시 실행할지를 정하기 때문에!!!!

 

hooks는 선언순서와 실행순서를 같도록 해야하기 때문에 무조건 최상위

조건문 안에 절대 넣으면 안됨

함수나 반복문 안에도 되도록 넣지 말 것


useEffect에서

componentDidMount는 실행하지 않고 componentDidUpdate만 실행하고 싶다!

이렇게 할 수 있음

const mounted = useRef(false);
useEffect(() => {
if (!mounted.current) {
  mounted.current = true;
} else {
  // 원하는 동작
}
}, [바뀌는값])

useMemo 복잡한 함수 결과값을 기억

useRef 단순한 결과값을 기억


useMemo 함수의 return값을 기억

useCallback 함수 자체를 기억


useCallback을 사용하면 좋은 점

함수 컴포넌트는 전체가 재실행되는데 함수 자체를 기억해둬서

컴포넌트가 새로 실행되어도 함수가 새로 생성되지 않는다

> 함수 생성 자체가 비용이 클 때 함수 자체를 기억해둬서 넣는 것

 

필수로 해야할 때 자식 컴포넌트에 props로 함수를 넘길 때는 useCallback 필수

> 하지 않으면 자식 컴포넌트가 계속 리렌더링 됨

 

새로운 값을 가져오는 경우에 useCallback을 사용하면 처음 값만을 기억하기 때문에 주의!

그래서 []input에 변하는 값을 알아채 새로 실행되도록 인자를 넣어줘야함


// hooks
// Lotto.jsx
import React, { useState, useRef, useEffect, useMemo,useCallback } from 'react';
import Ball from './Ball-class';

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 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([]);

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

  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]);
  
  return (
    <>
      <div>당첨숫자</div>
      <div id="결과창">{winBalls.map((v) => <Ball key={v} number={v} />)}</div>
      <div>보너스</div>
      {bonus && <Ball number={bonus} />}
      {redo && <button onClick={onClickRedo}>한 번 더!</button>}
    </>
  );
}

export default Lotto;
// Ball.jsx
import React, {memo} from 'react';

const Ball = memo(({ number }) => {
    let background;
    if (number <= 10) {
      background = 'red';
    } else if (number <= 20) {
      background = 'orange';
    } else if (number <= 30) {
      background = 'yellow';
    } else if (number <= 40) {
      background = 'blue';
    } else {
      background = 'green';
    }
    return (
      <div className="ball" style={{ background }}>{number}</div>
    );
});

export default Ball;
728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함