Notice
Recent Posts
Recent Comments
Link
«   2025/10   »
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
Tags
more
Archives
Today
Total
관리 메뉴

리액트 정리

[로또추첨기] useEffect, useMemo, useCallback 코드부분 + Hooks 코드 완성 + Class 코드 본문

리액트/웹게임

[로또추첨기] useEffect, useMemo, useCallback 코드부분 + Hooks 코드 완성 + Class 코드

버그킴 2020. 2. 27. 14:20

1. useEffect

  1. effect = componentDidMount로 수행할 기능
  2. cleanup = componentWillUnmount로 클린업 할 기능
  3. input = componentDidUpdate 조건을 넣어줌. 조건에 부합할 때 componentDidMount + componentDidUpdate 까지실행 (빈 배열이면 componentDidMount만 실행)
  useEffect(() => {
    effect
    return () => {
      cleanup
    };
  }, [input]) 

 

작성한 코드 부분

const Lotto = () => {
  const [selectedNumbers, setSelectedNumbers] = useState(getSelectedNumbers());
  console.log(selectedNumbers);
  const [winningNumberArr, setWinningNumberArr] = useState([]);
  const [bonusNumber, setBonusNumber] = useState(null);
  const [redo, setRedo] = useState(false);
  const timeouts = useRef([]);

  useEffect(() => {
    console.log('useEffect');
    for (let i = 0; i < selectedNumbers.length - 1; i++) {
      timeouts.current[i] = setTimeout(() => {
        setWinningNumberArr(prevState => {
          return [...prevState, selectedNumbers[i]];
        });
      }, (i + 1) * 1000);
    }
    timeouts.current[6] = setTimeout(() => {
      setBonusNumber(selectedNumbers[6]);
      setRedo(true);
    }, 7000);
    return () => {
      timeouts.current.forEach(v => {
        clearTimeout(v);
      });
    };
  }, [timeouts.current]); // input이 빈 배열이면 componentDidMount와 동일하다.

....

 

 

 

2. Memoization 

Hooks는 기본적으로 함수 컴포넌트 전체가 다같이 재실행되는데,

useMemo, useCallback 사용하면 복잡한 함수 실행한 결과값, 함수를 저장해둘 수 있다.

Hooks안에 함수가 있으면, 기본적으로 console.log하나씩 다 넣어놓고, 진짜 필요할때만 실행되는게 맞는지 확인하고 메모이제이션.

 

2-1. useMemo

useMemo: 함수의 리턴 값을 기억.

 

[]배열에 들어간 요소가 바뀌기 전까지 계속 같은 값을 갖고있는다. 콘솔찍어서 확인하면 한번만 실행됨!

 

  const lottoNumbers = useMemo(() => getSelectedNumbers(), []);

 

2-2. useCallback

 useCallback: 함수 자체를 기억. 부모가 자식에게 prop으로 함수를 내릴 때 사용. (안하면 계속 새 함수로 알고 리렌더링함. )

  const onClickRedo = useCallback(() => {
    console.log('useCallback');
    console.log(selectedNumbers);
    setSelectedNumbers(getSelectedNumbers());
    setWinningNumberArr([]);
    setBonusNumber(null);
    setRedo(false);
    timeouts.current = [];
  }, [selectedNumbers]);

 

 

 

 

코드 완성분

더보기

LottoHooks.jsx

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

function getSelectedNumbers() {
  console.log('getSelectedNumbers');
  const candidate = Array(45)
    .fill()
    .map((v, i) => {
      return i + 1;
    });
  const shuffle = [];
  while (candidate.length > 0) {
    shuffle.push(candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]);
  }
  const winningNumberArr = shuffle.slice(0, 6).sort((a, b) => {
    return a - b;
  });
  const bonusNumber = shuffle.pop();

  return [...winningNumberArr, bonusNumber];
}

const Lotto = () => {
  // Hooks는 기본적으로 함수 컴포넌트 전체가 다같이 재실행되는데, useMemo를 사용하면 복잡한 함수 실행한 결과값을 저장해둘 수 있다.
  // useMemo: 함수의 리턴 값을 기억. ([...winningNumberArr, bonusNumber])
  // []배열에 들어간 요소가 바뀌기 전까지 계속 같은 값을 갖고있는다. 콘솔찍어서 확인하면 한번만 실행됨!
  // Hooks안에 함수가 있으면, 기본적으로 console.log하나씩 다 넣어놓고, 진짜 필요할때만 실행되는게 맞는지 확인하고 메모이제이션.

  // useMemo : 함수의 리턴 값 기억 / useCallback: 함수 자체를 기억
  const lottoNumbers = useMemo(() => getSelectedNumbers(), []);
  const [selectedNumbers, setSelectedNumbers] = useState(lottoNumbers);
  const [winningNumberArr, setWinningNumberArr] = useState([]);
  const [bonusNumber, setBonusNumber] = useState(null);
  const [redo, setRedo] = useState(false);
  const timeouts = useRef([]);

  useEffect(() => {
    console.log('useEffect');
    for (let i = 0; i < selectedNumbers.length - 1; i++) {
      timeouts.current[i] = setTimeout(() => {
        setWinningNumberArr(prevState => {
          return [...prevState, selectedNumbers[i]];
        });
      }, (i + 1) * 1000);
    }
    timeouts.current[6] = setTimeout(() => {
      setBonusNumber(selectedNumbers[6]);
      setRedo(true);
    }, 7000);
    return () => {
      timeouts.current.forEach(v => {
        clearTimeout(v);
      });
    };
  }, [timeouts.current]); // input이 빈 배열이면 componentDidMount와 동일하다.

  // useCallback
  const onClickRedo = useCallback(() => {
    console.log('useCallback');
    console.log(selectedNumbers);
    setSelectedNumbers(getSelectedNumbers());
    setWinningNumberArr([]);
    setBonusNumber(null);
    setRedo(false);
    timeouts.current = [];
  }, [selectedNumbers]);

  return (
    <>
      <p>Winning Numbers</p>
      <div id='resultScreen'>
        {winningNumberArr.map(v => (
          <Ball key={v} number={v} />
        ))}
      </div>
      <p>Bonus Number</p>
      {bonusNumber && <Ball number={bonusNumber} />}
      {redo && <button onClick={redo ? onClickRedo : () => {}}>one more time? </button>}
    </>
  );
};

export default Lotto;

 

LottoClass.jsx

import React, { Component } from 'react';
import Ball from './Ball';

function selectedNumbers() {
  console.log('selectedNumbers');

  const candidate = Array(45)
    .fill()
    .map((v, i) => {
      return i + 1;
    });

  const shuffle = [];
  while (candidate.length > 0) {
    shuffle.push(candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]);
  }

  const winningNumberArr = shuffle.slice(0, 6).sort((a, b) => {
    return a - b;
  });

  const bonusNumber = shuffle.pop();

  return { winningNumberArr: winningNumberArr, bonusNumber: bonusNumber };
}

class Lotto extends Component {
  state = {
    selectedNumbers: selectedNumbers(),
    winningNumberArr: [],
    bonusNumber: null,
    redo: false,
  };

  timeouts = [];

  runTimeouts = () => {
    const { selectedNumbers } = this.state;

    for (let i = 0; i < selectedNumbers.winningNumberArr.length; i++) {
      this.timeouts[i] = setTimeout(() => {
        this.setState(prevState => {
          return { winningNumberArr: [...prevState.winningNumberArr, selectedNumbers.winningNumberArr[i]] };
        });
      }, (i + 1) * 1000);
    }
    this.timeouts[6] = setTimeout(() => {
      this.setState({
        bonusNumber: selectedNumbers.bonusNumber,
        redo: true,
      });
    }, 7000);
  };

  // 여기서 숫자 하나씩 나오게.. 1번 .. 1, 2번... 1,2,3번...
  componentDidMount() {
    if (this.state.winningNumberArr.length === 0) {
      this.runTimeouts();
    }
  }

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

  onClickRedo = () => {
    this.setState({
      selectedNumbers: selectedNumbers(),
      winningNumberArr: [],
      bonusNumber: null,
      redo: false,
    });

    this.timeouts = [];
    this.runTimeouts();
  };

  render() {
    const { winningNumberArr, bonusNumber, redo } = this.state;
    return (
      <>
        <p>winning numbers</p>
        <div id='resultScreen'>
          {winningNumberArr.map(v => (
            <Ball key={v} number={v} />
          ))}
        </div>
        <p>Bonus number</p>
        {bonusNumber && <Ball number={bonusNumber} />}
        {redo && <button onClick={redo ? this.onClickRedo : () => {}}>one more time? </button>}
      </>
    );
  }
}

export default Lotto;

 

Ball.jsx

import React, { memo } from 'react';

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

export default Ball;