기술/React

[React] useState - 컴포넌트와 상태

leedaramji 2024. 2. 27. 00:32

컴포넌트와 상태

 

state는 동적인 리액트 컴포넌트로 state의 값에 따라 다른 결과를 렌더링 한다. 사용자의 행위나 시간 변동에 따라 값이 변한다. 

 

const [ state, setState ] = useState(초깃값);

 

  • state : state 변수, 형재 상태
  • setState : set 함수, state를 변경하는 함
  • useState() : 생성자(초깃값)

 

useState 인자로 값을 넣어주면 state와 setState의 2가지 요소를 배열 형태로 리턴해 준다. state를 변경시켜 주고 싶을 땐 setState 함수를 이용해서 간편하게 변경시켜 줄 수 있다.

 

const [ light, setLight ] = useState(4);

 

  • State : light = 4
  • setLight(5) ➡️ light = 5
  • setState 함수로 state를 변경하면 해당 컴포넌트는 화면에 다시 업데이트(렌더링) 된다.

 


 

🌏 [set 함수로 State 값 변경하기]

import { useState } from "react";

function Body () {
    const [count, setCount] = useState(0); 📌
    
    const onIncrease = () => {	📌
    	setCount(count + 1);
    };
    
    return (
    	<div>
            <h2>{count}</h2>
            <button onClick={onIncrease}>클릭</button> 📌
        </div>
    );
}
export default Body;

 

  • 버튼의 이벤트 핸들러 onIncrease에서는 set 함수인 setCount를 호출한다. 인수로 count에 1 더한 값을 전달한다.
  • set 함수를 호출해 State 값을 변경하면, 변경값을 페이지에 반영하기 위해 컨포넌트를 다시 렌더링한다.
  • 컴포넌트가 페이지에 렌더링하는 값은 컴포넌트 함수의 반환값이다.
  • 컴포넌트를 다시 렌더링 = 컴포넌트 함수를 다시 호출한다는 의미

 

 

  • 컴포넌트는 자신이 관리하는 State 값이 변하면 다시 호출된다.
  • 그리고 변경된 State 값을 페이지에 렌더링한다.
  • State 값이 변해 컴포넌트를 다시 렌더링하는 것을 '리렌더' 또는 '리렌더링'이라고 한다.
  • 리액트 컴포넌트는 자신이 관리하는 State 값이 변하면 자동으로 리렌더된다.

 


 

  • <input> 태그로 텍스트 입력하기
    • <input>은 폼이 다양해서 type 속성을 지정하여 사용
    • text, date, tel, radio, checkbox etc
    • 아무 것도 입력하지 않은 기본 입력 폼은 text

 

  • <input> 태그로 날짜 입력하기
    • type="date"
    • e.target.value에는 yyyy-mm-dd 형식의 날짜 저장

 

  • 드롭다운 상자로 여러 옵션 중에 하나 선택하기
    • <select>와 <option> 함께 사용
    • 목록 하나를 선택하면 해당 항목 입력할 수 있다.
    • option에 key={"값"} 속성 설정해줘야 한다.
    • e.target.value에는 현재 사용자가 선택한 옵션의 key 저장

 

  • 글상자로 여러 줄의 텍스트 입력하기
    • <textarea>
    • <input> 태그의 입력 폼과 동일한 형태
    • e.target.value에 저장된 값을 인수로 전달해 state 값 변경

 


 

🌏 [여러 개의 사용자 입력 관리하기]

 

1) 관리할 state 수가 많아 코드가 길어진다.

import { useState } from "react";

function Body() {
    const [name, setName] = useState("");
    const [gender, setGender] = useState("");
    const [birth, setBirth] = useState("");
    const [bio, setBio] = useState("");
    
    const onChangeName = (e) => {
    	setName(e.target.value);
    };
    const onChangeGender = (e) => {
    	setGender(e.target.value);
    };
    const onChangeBirth = (e) => {
    	setName(e.target.value);
    };
    const onChangeBio = (e) => {
    	setName(e.target.value);
    };
    
    return (
    	<div>
        	<div>
            	<input value={name} onChange={onChangeName} placeholder"이름" />
            </div>
            <div>
            	<select key={gender} onChang={onChangeGener}>
                    <option key={""}></option>
                    <option key={"남성"}>남성</option>
                    <option key={"여성"}>여성</option>
                </select>
            </div>
            <div>
            	<input type="date" value={birth} onChange={onChangeBirth} />
            </div>
            <div>
            	<textarea value={bio} onChange={onChangeBio} />
            </div>
        </div>
    );
}
export default Body;

 

2) 객체 자료형을 이용해서 하나의 state로 관리

import { useState } from "react";

function Body() {
    const [state, setState] = useState({	📌 1
    	name: "",
        gender: "",
        birth: "",
        bio: "",
    });
    
    const handleOnChange = (e) => {
    	console.log("현재 수정 대상:", e.target.name);
        console.log("수정 값:", target.value);
        setState({				📌
        	...state,
        	[e.target.name]: e.target.value,
        });
    };
    
    return (
    	<div>
        	<div>
            	<input 
                	name="name"	📌 2
                	value={state.name} 📌 3
                    onChange={handleOnChange} 📌 4
                    placeholder="이름" 
                />
            </div>
            <div>
            	<select name="gender" key={state.gender} onChang={handleOnChange}>
                    <option key={""}></option>
                    <option key={"남성"}>남성</option>
                    <option key={"여성"}>여성</option>
                </select>
            </div>
            <div>
            	<input 
                	name="birth"
                	type="date" 
                    value={state.birth} 
                    onChange={handleOnChange} />
            </div>
            <div>
            	<textarea name="bio" value={state.bio} onChange={handleOnChange} />
            </div>
        </div>
    );
}
export default Body;

 

  1. 객체 자료형으로 State를 하나 생성하고 초깃값을 설정한다.
  2. 모든 입력 폼에 name 속성을 지정
    • name="gender"
  3. 모든 입력 폼의 value 를 객체 state의 프로퍼티 중 하나로 설정
    • 예를 들면, value={state.gender} 지정
    • 객체 state의 name 프로퍼티와 동일한 값으로 설정
  4. 사용자의 입력을 처리할 이벤트 핸들러 설정

 

setState({			
	...state,
	[e.target.name]: e.target.value,
});

 

  • 함수 setState에서는 새로운 객체를 생성해 전달한다.
  • 스프레드 연산자를 이용해 기존 객체 state의 값 나열(...)
  • 객체의 괄호 표기법을 사용하여 입력 폼의 name 속성(e,target.name)을 key로 설정
  • 입력폼에 입력한 값(e.target.value)을 value로 사용

 

💡

  • e.target.name이 현재 이벤트 발생한 요소의 name 속성
    • 만약 <select>에서 이벤트가 발생했다면, e.target.name은 gender가 된다.  
    • 객체 state의 프로퍼티 중 현재 이벤트가 발생한 요소인 프로퍼티의 value 값을 변경하게 된다.

 


 

🌏 [Props와 State]

 

동적으로 변하는 값인 리액트의 State 역시 일종의 값이므로 Props로 전달할 수 있다.

 

import { useState } from "react";

function Viewer({ number }) {	📌
	return <div>{number % 2 === 0 ? <h3>짝수</h3> : <h3>홀수</h3>}</div>
}

function Body() {
	const [number, setNumber] = useState(0);
    const onIncrease = () => {
    	setNumber(number + 1);
    };
    const onDecrease = () => {
    	setNumber(number - 1);
    };
    return (
    	<div>
        	<h2>{number}</h2>
            <Viewer number={number} />	📌
            
            <div>
            	<button onClick={onDecrease}>-</button>
                <button onClick={onInCrease}>+</button>
            </div>
        </div>
    );
}
export default Body;

 

  • Viewer 컴포넌트를 선언한다. 이 컴포넌트에는 Props로 Body 컴포넌트에 있는 State 변수 number가 전달된다.
  • Viewer 컴포넌트는 조건부 렌더링을 이용해 변수 number의 값을 평가하고, 값에 따라 짝수 혹은 홀수 값을 페이지에 렌더링한다.
  • Body에서 Viewer를 자식 컴포넌트로 사용하며, Props로 변수 number를 전달한다.

 

📒 알 수 있는 중요한 사실 


자식 컴포넌트는 Props로 전달된 State 값이 변하면 자신도 리렌더된다는 사실이다. 즉, 부모에 속해 있는 State(number) 값이 변하면 Viwer 컴포넌트에서 구현한 '짝수', '홀수' 값도 따라서 변한다.

 

 

 

⚠️ 나만 알아 보는 코드.....뜯어 보기........... 연습^^!!!!!!!

 


import { useState } from "react";

const heavyWork = () => {	📌무거운 작업을 하는 함수
	return ['콩', '두부'];
};

function App () {
    const [names, setNames] = useState(() => {	📌콜백 함수를 넣어 리턴 값으로 무거운 작업을 하는 함수를 불러 준다.
    	return heavyWork();
    }); 
    const [input, setInput] = useState('');
    
    const handleInputChange = () => {
    	setInput(e.target.value);
    };
    
    const handleUpload = (prevState) => {	📌
    	setNames(() => {
        	return([input, ...prevState]);
        });
    };
    
    return (
    	<div>
            <input type="text" value={input} onChange={handleInputChange} />
            <button onClick={handleUpload}>업로드</button>
            {names.map((name, idx) => {
            	return <p key={idx}>{name}</p>;
            })}
        </div>
    );
}
export default App;

 

[정리]

 

1)

setState((prevState) => {
    // some works...
    return newState;
});

 

  • state를 변경할 때 새로 변경될 state 값이 이전 state 값과 연관이 되어 있다면 setState의 인자로 새로운  state를 리턴하는 콜백 함수를 넣어주는 것이 좋다.

 

2)

useState(() => {
	return heavyWorks();
})

 

  • useState를 사용해서 초깃값을 받아올 때 무거운 작업을 해야 한다면 useState의 인자로 콜백 함수를 넣어 준다면 맨 처음 렌더링이 될 때만 실행되게 할 수 있다.

 

 

 

📕참고: 한 입 크기로 잘라 먹는 리액트 - 이정환

📕참고: useState _유튜브 별코딩