본문 바로가기
패스트캠퍼스/자습

리액트 46~50강 정리

by sunnykim91 2019. 10. 1.

 

for문 안에 비동기함수를 사용할때  변수로 let을 선언하게 되면 let은 블록레벨 스코프를 지원하기 때문에 클로저문제를 해결 할 수 있음.

 

 

Lotto class형

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,
    };

    onclickRedo = () => {
        this.setState({
            winNumbers:  getWinNumbers(),  // 당첨 숫자들
            winBalls: [],
            bonus: null,  // 보너스공
            redo: false,
        });
        this.timeouts = [];
    };

    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,
            });
        }, 7000);
    }

    componentDidMount() {
        this.runTimeouts();
    }

    componentDidUpdate(prevProps, prevState) {  // 바뀌기 이전 상태가 prevState, 바뀌고 난 후 상태가 this.state에 들어있음. 
        if(this.state.winBalls.length === 0 ){   //  조건문이 중요한 역할을 함. setState가 실행될때마다 이 메소드가 실행되기 떄문에 조건문을 적용해서 상황에 알맞게 실행
            this.runTimeouts();
        }
    }

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

    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;

 

 

Lotto hooks

import React, { useState, useRef, useEffect, useMemo , useCallback} 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];
}

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

    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 둘 다 수행

    const onclickRedo = useCallback(() => {
        setWinNumbers(getWinNumbers());
        setWinBalls([]);
        setBonus(null);
        setRedo(false);
        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;

 

 

hooks에서는 전체가 계속 다시 실행 되기 때문에 특정 필요없는 부분도 계속 실행되기 마련이다.

그래서 이런 문제를 해결하기 위해서 useMemo를 사용한다.

 

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

useRef는 일반 값을 기억

usecallback은 함수를 기억

 

useMemo나 useCallback의 2번재 인자는 바뀌는 값들을 넣어준다.

 

자식컴포넌트에 props로 함수를 넘길때는 useCallback을 해주어야한다. 이유는?

부모가 매번 새로운 함수를 주네 ? (함수는 바뀐게 없는데) 새로운 함수를 주니까 계속 자식이 리랜더링을 함.

자식이 쓸데없이 리랜더링을 안함.

 

 

Hooks에서는 조건문이나 함수 등에 넣지 말고, 최상위에 두는 것이 좋다. 

 

useMemo : 값을 저장(기억)한다. 리턴값을 두번째 인자가 바뀌기 전까지

useEffect : 내부를 실행한다. 두번째가 인자가 바뀔때

useCallback : 함수 자체를 기억한다. 두번째 인자가 바뀌기 전까지

 

 

 

반응형

'패스트캠퍼스 > 자습' 카테고리의 다른 글

리액트 51~55강 정리  (0) 2019.10.03
poiemaweb 20~21강 복습  (0) 2019.10.01
poiemaweb 18~19강 복습  (0) 2019.09.30
리액트 41~45강 정리  (0) 2019.09.30
poiemaweb 16~17강 복습  (0) 2019.09.23