본문 바로가기
CodeIt_Sprint/React_초급

(9)React_초급_JSX 문법6 : useState(기본자료형) vs useState(참조자료형)

by 코잼민 2024. 11. 23.

● Defalut 상황

○ Dicegame 프로젝트에서, useState()메소드를 이용하여, 돌렸을 때마다, " 총점 , 기록 " 을 하는 항목 추가해보기 

- App.js :

import { useState } from 'react';
import Button from './Button';
import Dice from './Dice';

function random(n) {
  return Math.ceil(Math.random() * n);
}

function App() {
  const [num, setNum] = useState(1);

  const handleRollClick = () => {
    const nextNum = random(6);
    setNum(nextNum);
  };

  const handleClearClick = () => {
    setNum(1);
  };

  return (
    <div>
      <div>
        <Button onClick={handleRollClick}>던지기</Button>
        <Button onClick={handleClearClick}>처음부터</Button>
      </div>
      <div>
        <h2>나</h2>
        <Dice color='blue' num={num} />
        /*
        <h2>총점</h2>
        <p>{sum}</p>
        <h2>기록</h2>
        {gameHistory.join(', ')}
        */
      </div>
    </div>
  );
}

export default App;

● 먼저 그냥 무근본이라도 작성해보기 :

import { useState } from 'react'; //useState 를 react 컴ㅍ넌트에서 불러오기
import Button from './Button';
import Dice from './Dice';

function App() {
  const [num, setNum] = useState(1);
  const [sum, setSum] = useState(0); //sum에 해당 useState 초기화
  const [Record, setRecord] = useState([]); //기록에 해당 배열 useState 초기화

  //onClick용 함수
  const handleRollClick = ()=>{
    const randNum = Math.floor(Math.random()*(6-0)) +1;
    setNum(randNum);//num을 3으로 변경해주는 메소드
    
    setSum(sum + randNum);//sum (초기 0) + 나온 randNum 값 추가

    Record.push(randNum);//던진 후, 나온 눈값 randNum을 배열에 push

    setRecord(Record);//배열 업데이트
  

  };

  //onClick 처음부터 메소드
  const setReset = ()=>{
    setNum(1);//num을 3으로 변경해주는 메소드
    setSum(0);//초기화 => 0값
    setRecord([]);//초기화 => 빈 배열
  };

  return (
    //.Array프로퍼티 메소드 join(', ')메소드를 이용하여, 배열에 원소 출력
    <>
      <div>
        <Button onClick={handleRollClick}>던지기</Button>
        <Button onClick={setReset}>처음부터</Button>
      </div>
      <Dice color="blue" num={num} />
      <p>총점 : {sum}</p>
      <p>기록 : {Record.join(', ')}</p> 
    </>
  )
}

export default App;

○ 출력 장면 :

 

● 알아야 할 개념 :

- 개념1_ 기본자료형 vs 참조자료형 차이

  • 기본 자료형 : 숫자 , 문자, bool
  • 참조 자료형 : Array, String , Date

예를 들어, 아래와 같이, 변수 선언 후, C언어에서 출력시

#include <stdio.h>

int main(){
	int var = 3;
    int arr = [1,3,4,5];
    
    printf("변수 : %d \n",var);//변수값이 출력된다.
    printf("배열 : %d \n",arr);//★인덱스0의 주소가 출력된다.
    
    
    return 0;
}

내 생각엔 저 위의 원리가 적용되는 거 같다.

- 개념2_ const [업데이트할 변수, set어쩌구메소드()] = useState(초기값);

업데이트할 변수 가 기본자료형 ,참조자료형에 따라 다르게 인식하게 된다.

"업데이트 변수 == 기본자료형" 에 대한 설명
"업데이트 변수 == 기본자료형" 에 대한 설명

● 그래서, 어떤 문제가 발생? => "총점은 ㅈ까고, 기록만 하고 싶을 때, 문제 발생"

import { useState } from 'react'; //useState 를 react 컴ㅍ넌트에서 불러오기
import Button from './Button';
import Dice from './Dice';

function App() {
  const [num, setNum] = useState(1);
  const [Record, setRecord] = useState([]); //기록에 해당 배열 useState 초기화

  //onClick용 함수
  const handleRollClick = ()=>{
    const randNum = Math.floor(Math.random()*(6-0)) +1;
    

    Record.push(randNum);//던진 후, 나온 눈값 randNum을 배열에 push

    setRecord(Record);//배열 업데이트
    
    console.log(Record);

  };

  //onClick 처음부터 메소드
  const setReset = ()=>{
    setNum(1);//num을 3으로 변경해주는 메소드
    setRecord([]);//초기화 => 빈 배열
  };

  return (
    //.Array프로퍼티 메소드 join(', ')메소드를 이용하여, 배열에 원소 출력
    <>
      <div>
        <Button onClick={handleRollClick}>던지기</Button>
        <Button onClick={setReset}>처음부터</Button>
      </div>
      <Dice color="blue" num={num} />
      <p>기록 : {Record.join(', ')}</p> 
    </>
  )
}

export default App;

○ 출력장면 :

=> 버튼을 계속 누를시 ,배열에 계속 추가되는 것은 console창에서 보면, 알수있지만, useState는 첫번째 인덱스의 원소||주소만 바꼈는지만 확인하여, ㅄ이 업데이트됐다고 생각안하고, 기록을 안함

● 해결방법 :

  • 초기화 단계는 그대로
  • onClick 메소드 작성시, 배열.push() 후, setRecord([...객체변수이름, 추가할 값])를 thread 문법으로 한다.
import { useState } from 'react'; //useState 를 react 컴ㅍ넌트에서 불러오기
import Button from './Button';
import Dice from './Dice';

function App() {
  const [num, setNum] = useState(1);
  const [Record, setRecord] = useState([]); //기록에 해당 배열 useState 초기화

  //onClick용 함수
  const handleRollClick = ()=>{
    const randNum = Math.floor(Math.random()*(6-0)) +1;
    
    setNum(randNum);

    setRecord([...Record,randNum]);//배열 업데이트
    

  };

  //onClick 처음부터 메소드
  const setReset = ()=>{
    setNum(1);//num을 3으로 변경해주는 메소드
    setRecord([]);//초기화 => 빈 배열
  };

  return (
    //.Array프로퍼티 메소드 join(', ')메소드를 이용하여, 배열에 원소 출력
    <>
      <div>
        <Button onClick={handleRollClick}>던지기</Button>
        <Button onClick={setReset}>처음부터</Button>
      </div>
      <Dice color="blue" num={num} />
      <p>기록 : {Record.join(', ')}</p> 
    </>
  )
}

export default App;

◎ 연습문제 :

주어진 getResult 함수를 사용해서 승리, 패배, 무승부에 해당하는 승부 결과를 만들 수 있었는데요.

이번 레슨에서는 이걸 누적해서 보여주는 승부 기록을 만들어 봅시다.

아래를 참고해서 App.js 파일을 수정해 주세요.

  • ○ handleButtonClick 함수에서 gameHistory state에 nextHistoryItem 을 새 요소로 추가해 주세요.
  • handleClearClick 함수에서는 gameHistory 를 빈 배열로 변경합니다.

 

=> 답 :

function App() {
  const [hand, setHand] = useState(INITIAL_VALUE);
  const [otherHand, setOtherHand] = useState(INITIAL_VALUE);
  const [gameHistory, setGameHistory] = useState([]);

  const handleButtonClick = (nextHand) => {
    const nextOtherHand = generateRandomHand();
    const nextHistoryItem = getResult(nextHand, nextOtherHand);
    //setHand(nextHand);
    //setOtherHand(nextOtherHand);
    // gameHistory에 nextHistoryItem 을 추가해 주세요
    setGameHistory([...gameHistory,getResult()]);
  };

  const handleClearClick = () => {
    setHand(INITIAL_VALUE);
    setOtherHand(INITIAL_VALUE);
    // gameHistory를 비워주세요
    setGameHistory([]);
  };

  return (
    <div>
      <Button onClick={handleClearClick}>처음부터</Button>
      <div>
        <HandIcon value={hand} />
        VS
        <HandIcon value={otherHand} />
      </div>
      <p>승부 기록: {gameHistory.join(', ')}</p>
      <div>
        <HandButton value="rock" onClick={handleButtonClick} />
        <HandButton value="scissor" onClick={handleButtonClick} />
        <HandButton value="paper" onClick={handleButtonClick} />
      </div>
    </div>
  );
}