Hooks 변경한 코드

import React, {Component, useState, useRef} from "react";
import TryHooks from "./TryHooks";

function getNumbers(){
    const numbers = new Array(9).fill(0).map((v,i)=>v+i+1);
    const shuffle = [];
    while(numbers.length > 0){
        const rand = Math.floor(Math.random() * numbers.length);
        const number = numbers.splice(rand,1)[0];
        shuffle.push(number);
    }
    console.log(shuffle);
    return shuffle.slice(0,4);
}

const NumberBaseballHooks = () => {
    const [value, setValue] = useState('');
    const [result, setResult] = useState('');
    const [answer, setAnswer] = useState(getNumbers());
    const [tries, setTries] = useState([]);
    const inputRef = useRef(null);
    const onSubmitForm = e => {
        e.preventDefault();
        const answerStr = answer.join('');
        if(answerStr === value){
            //홈런
            setResult('홈런!');
            setValue('');
            setTries((prev)=>{
                return [...prev, `${value}:홈런!`]
            });
            setTimeout(()=>{
                alert('게임을 다시 시작합니다.');
                setResult('');
                setAnswer(getNumbers());
                setTries([]);
            },100);

        } else {
            if(tries.length >= 9){
                setResult(`10번 넘게 틀려서 실패! 정답은 ${answerStr} 였습니다`);
                setValue('');
                alert('게임을 다시 시작합니다.');
                setResult('');
                setAnswer(getNumbers());
                setTries([]);
            }else{
                //볼, 스트라이크 판단
                let ball = 0;
                let strike = 0;
                for(let i=0; i<4; i++){
                    if(answerStr[i] === value[i]){
                        strike++;
                    }else if(answerStr.includes(value[i])){
                        ball++;
                    }
                }
                setTries((prev)=>{
                    return [...prev, `${value}:${strike}S ${ball}B`];
                })
                setResult('땡');
                setValue('');
            }
        }
        inputRef.current.focus();
    }

    const onChangeInput = e => {
        const value = e.target.value;
        setValue(value);
    }

    return (
        <>
            <h1>{result}</h1>
            <form onSubmit={onSubmitForm}>
                <input type="text" maxLength={4} ref={inputRef} value={value} onChange={onChangeInput}/>
                <button>입력</button>
            </form>
            <div>시도: {tries.length}</div>
            <ul>
                {
                    tries.map((v,i) => {
                        return (<TryHooks key={v+i} value={v} index={i+1} />);
                    })
                }
            </ul>
        </>
    )
}
export default NumberBaseballHooks;

NumberBaseballHooks.jsx

 

import React from "react";

const TryHooks = ({index, value}) => {
    return (
        <li>
            {index}. <b>{value}</b>
        </li>
    )
}
export default TryHooks;

TryHooks.jsx

 

 

근데 여기서 문제가 있었다.

input에 value를 변경하면 setValue가 value state를 변경하면서 리렌더가 되는데 리렌더가 되면서 NumberBaseballHooks 내부의 로직이 재실행 되면서 getNumbers()까지 재호출 되는 문제가 있었다.

그래서 위의 사진과 같이 console.log가 input에 타자 칠 때마다 나오는 것.

 

const [answer, setAnswer] = useState(getNumbers());

해당 코드가 문제였는데 저렇게 한다고해서 state가 리렌더 할 때마다 바뀌는 것은 아니고 호출만 여러 번 되는것이다.

하지만 호출이 되는 것도 문제이다. 

useState가 자동으로 getNumbers()를 첫 번째 호출한 리턴값만 state에 저장하고 그 다음부터는 리턴값을 무시해버린다. 하지만 리렌더 시에 getNumbers함수가 계속 재호출 되긴한다. (호출값을 넣어버렸기 때문에) 만약 getNumbers가 아주 오래걸리는 작업을 하는 함수라면 매우 비효율적이다.

 

그럴 때는 아래와 같이 호출해서 넘기지 말고 함수 자체를 넘기면 된다. (lazy init 이라고 한다.)

const [answer, setAnswer] = useState(getNumbers);

 

useState 용법이 2가지고 있는데

1. useState(초기값) - 일반적으로 쓰이는 경우로 초기값으로 state를 설정해준다.

2. useState(함수) - 함수를 그대로 넣는 경우인데 함수의 리턴값이 초기값으로 설정이 되지만 그 다음부터 이 함수는 실행되지 않는다. 

+ Recent posts