개발노트

12. componentDidUpdate , setInterval 본문

React/basic

12. componentDidUpdate , setInterval

aloha2jh 2020. 7. 27. 23:18

(zerocho)react기초 게임 예제 - 로또추첨기

 

1~45까지 숫자만들어서 섞어서 7개 반환하는 함수

function getPickNumbers(){
    console.log('pickNum START');
    const numArray = Array(45).fill().map( (v,i) => i + 1 );  // 1~45정수 배열 만듬.
    const shuffle = [];
    while( numArray.length>0 ){   //1~45를 랜덤하게 섞기 위해서
        shuffle.push( numArray.splice(    Math.floor( Math.random() * numArray.length )   ,1)[0]  );
        // (1~10랜덤) * 45  한다음 45,44 랭스 바뀌게 이렇게 그숫자 하나씩 제거  (곱하는건왜하는건지? 알고리즘인데,)
        //그숫자 push
    }
    const bonusNumber = shuffle[shuffle.length -1]; //섞어서 마지막은 보너스번호로 픽-
    const rltNumbers = shuffle.slice(0,6).sort( (p,c)=>p-c );  //섞었으니앞에서부터 그냥 여섯개뽑고, 쏘팅
    return[...rltNumbers, bonusNumber]; // 여섯개, 보너스1 해서 반환. 
};

 

jsx 

balls (array) 갯수만큼 무조건 화면에 보여주게

 //Lotto.jsx
            <>
                <h1>당첨숫자</h1>
                <ul id="rlt_box">
                    {  balls.map( ( v )=>( <Ball key={v} num={v} /> ) )} 
                     
                    { bonus &&  <Ball num={bonus} />  }
                </ul>  
                { rePick && <button onClick={ rePick ? this.onClickRePick :()=>{} } >한번더</button> }
            </>
       
//Ball.jsx
return(
        <li className='ball' style={{ background: `${bg}` }}>{ num }</li>
    )

 

상태값 변경 작업 componentDidMount

번호 7개를 다 뽑아둔 다음, pickNumbers (array)

setTimeout 으로 (1+0), (1+1), (1+2)... 초 마다 

balls Array에다 1개 1개 추가해줌. balls(array)가 화면에 보이는 볼들,

    componentDidMount(){
        const {pickNumbers}=this.state;
        for( let i=0; i<pickNumbers.length-1; i++){

            setTimeout( ()=>{   
                this.setState( (prevState)=>{
                    return{
                        balls: [ ...prevState.balls, pickNumbers[i] ],  ////
                    }
                })
            } , (i+1)*100 ) ;
        }
        setTimeout(
            ()=>{
                this.setState({
                    bonus: pickNumbers[6],
                    rePick:true
                })
            } 
            ,700
        )
    }

push해서 하나씩 더 추가하는것 이기 때문에 prevState사용.

 

 

 

 

부모컴포넌트가 자식컴포넌트를 없앨때는  setTimeout을 클리어 해줘야 된다.

메모리문제, 해당컴포넌트가 없어졌는데 setState(또는setTimeout) 하면 안되기 때문에

이런 논리적이지 않은 코드로인해 의도치않은 에러가 생길수 있음 

setTimeout을 썻으면 clear해줘야함

 

 

 

갯수만큼 클리어 componentWillUnMount

    timer = []; ///

    componentDidMount(){
        const {pickNumbers}=this.state;
        for( let i=0; i<pickNumbers.length-1; i++){

            this.timer[i] = setTimeout( ()=>{  ///
                this.setState( (prevState)=>{
                    return{
                        balls: [ ...prevState.balls, pickNumbers[i] ],
                    }
                })
            } , (i+1)*100 ) ;
        } 
    }
    
    componentWillUnmount(){
        this.timer.forEach( (v)=>{ clearTimeout(v) }); ///
    }

pickNumbers의 length만큼 반복시켜 발생시킨 setTimeout을 clear하기 위해,

새 배열을 만들어 발생시킬때마다 담아준다

unmount에 forEach반복문으로 timer갯수만큼 clear시켜준다

setTimeout발생후, 바로 삭제(부모컴포넌트가자식컴포넌트를삭제)될수도 있으니 갯수만큼 삭제

 

 

 

다시실행 componentDidUpdate

 onClickRePick = () =>{
        //초기화
        this.setState({ 
            pickNumbers: getPickNumbers(),  // 6
            balls:[],
            bonus: null,
            rePick: false,
        });
        //timer
        this.timer=[];
    }

(1)초기화  그래고 (2)componentDidMount코드로 똑같이 실행 시키면 된다.

 

componentDidUpdate를 사용하면 (초기값으로)상태값을 바꾼거를

어떤어떤게 바꼈는지 여기서 판단할 수 있다.

 

바뀌기전 state는 prevState,

바뀐후 state는 this.State

prevProps는 부모한테서받은 props이 바뀔수도 있어서

 

 

 

 

클릭했을때 balls값들을 비워주니까

만약 balls이 비었으면 로또를 다시 시작해주라고 할수 있다.

    componentDidUpdate( prevProps, prevState){
        if( this.state.balls.length === 0 ){
            this.lottoStarting ();
        }
    }

 

 

조건을 꼭 써야 되는데, DidUpdate 이벤트가 setState가 일어날때마다 발생하기 때문에,

onClickRePick버튼눌렀을때만 발생할 수 있도록 ===0일때로 걸어준것.

 

 

 

 

 

 

 

React.memo컴포넌트

Ball.jsx에 memo컴포넌트를 넣지않을경우 이렇게 자식들한테 렌더링이 발생

 

 

 

메모넣을경우 

 

 

//Ball.jsx
import React from 'react';

const Ball = React.memo(  ({ num }) =>{
    let bg; 
    if( num < 10 ){
        console.log('1');
        bg = '#ffd700';
    }else if( num <= 20 ){
        bg = '#ff972c'; 
    }else if( num <= 30 ){
        bg = '#8bc34a'; 
    }else if( num <= 40 ){
        bg = '#009688'; 
    }else{
        bg = '#fb7fd5';
    }
    return(
        <li className='ball' style={{ background: `${bg}` }}>{ num }</li>
    )
})


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

function getPickNumbers(){
    console.log('pickNum START');
    const numArray = Array(45).fill().map( (v,i) => i + 1 );   
    const shuffle = [];
    while( numArray.length>0 ){    
        shuffle.push( numArray.splice(    Math.floor( Math.random() * numArray.length )   ,1)[0]  ); 
    }
    const bonusNumber = shuffle[shuffle.length -1]; 
    const rltNumbers = shuffle.slice(0,6).sort( (p,c)=>p-c );  
    return[...rltNumbers, bonusNumber];  
};

class Lotto extends Component {
    state = {
        pickNumbers: getPickNumbers(),  // 6
        balls:[],
        bonus: null,
        rePick: false,
    }

    timer = [];

    lottoStarting = ()=>{
        const {pickNumbers}=this.state;
        for( let i=0; i<pickNumbers.length-1; i++){

            this.timer[i] = setTimeout( ()=>{  ///
                this.setState( (prevState)=>{
                    return{
                        balls: [ ...prevState.balls, pickNumbers[i] ],
                    }
                })
            } , (i+1)*100 ) ;
        }
        this.timer[6] = setTimeout(  //얘도setImterval이니까  clear해주기위해담는다.
            ()=>{
                this.setState({
                    bonus: pickNumbers[6],
                    rePick:true
                })
            } 
            ,700
        )
    }

    componentDidMount(){
        this.lottoStarting ();
    }
    
    componentWillUnmount(){
        this.timer.forEach( (v)=>{ clearTimeout(v) });
    }

    onClickRePick = () =>{
        //초기화
        this.setState({ 
            pickNumbers: getPickNumbers(),  // 6
            balls:[],
            bonus: null,
            rePick: false,
        });
        //timer
        this.timer=[];
    }

    componentDidUpdate( prevProps, prevState){
        if( this.state.balls.length === 0 ){
            this.lottoStarting ();
        }
    }

    render(){
        const{ balls, bonus , rePick } = this.state;
        return(
            <>
                <h1>당첨숫자</h1>
                <ul id="rlt_box">
                    {  balls.map( ( v )=>( <Ball key={v} num={v} /> ) )} 
                     
                    { bonus &&  <Ball num={bonus} />  }
                </ul>  
                { rePick && <button onClick={ rePick ? this.onClickRePick :()=>{} } >한번더</button> }
            </>
        )
    }
}
export default Lotto;
<html lang="ko">
    <head>
        <meta charset="utf-8">
        <title>game</title>
    </head>
    <style>
        ul{ width:100%; float:left;}
        .ball {
            list-style: none;
            float: left;
            border: 1px solid white;
            border-radius: 20px;
            width: 40px;
            height: 40px;
            color: #fff;
            FONT-WEIGHT: 700;
            line-height: 40px;
            font-size: 20px;
            text-align: center;
            margin-right: 20px;
        }
    </style>
    <body>
        <div id="root"></div>
        <script src="./dist/app.js"> 
        </script>
    </body>
</html>

 

'React > basic' 카테고리의 다른 글

13. useReducer  (0) 2020.07.28
12. hooks / useEffect , useMemo  (0) 2020.07.28
11 (2)hooks / useEffect  (0) 2020.07.26
11. react life cycle  (0) 2020.07.26
10. (4)jsx 에서 for, if  (0) 2020.07.26