본문 바로가기

P&P 학회/FRONT-END STUDY

4주차 (2024.10.28)

ReactJS 입문 2

1. State

State란 현재 가지고 있는 형태나 모양을 정의 변화할 수 있는 동적인 값을 뜻한다.

State를 갖는 컴포넌트들을 활용하여 State의 값에 따라 각각 다른 UI를 화면에 렌더링한다. 

 

1.1. State 실습

함수컴포넌트에서 state를 생성하기 위해서 리액트가 제공하는 내장함수를 사용하여야 한다.

1) import { useState } from "react"; 이 문장을 입력해 useState 함수 불러오기

2) 컴포넌트 내에서 useState() 입력하여 호출

반환값을 state라는 변수에 담는 것

useState는 두 개의 요소를 담은 배열을 반환한다.

1번째 요소: 새롭게 생성된 state의 값

2번째 요소: state의 값을 변경하는 함수

배열을 받기 때문에 보통 반환값을 변수에 저장하는 방식인 const state = useState(); 문법을 이용하지 않고 배열의 비구조화 할당 문법 const [count, setCount] = useState(0);을 사용할 수 있다.

더보기

EXAMPLE (버튼을 클릭했을 때 화면에 렌더링 되는 숫자가 하나씩 증가하는 코드)

import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
    <div>
      <h1>{count}</h1>
        <button
          onClick={() => {
          setCount(count + 1);
        }}
        >
          +
        </button>
    </div>
    </>
  )
}

export default App;

 

만약 변수가 계속 변하더라도 state 값이 아닌 일반 변수로 선언하여 변경시킬 경우 state값에 변화가 없기 때문에 리렌더링이 되지 않는다.

더보기

EXAMPLE (끄기/켜기 변화값 렌더링 코드)

import "./App.css";
import { useState } from "react";

function App() {
  const [light, setLight] = useState("OFF");
    // let light = "OFF";

  return (
    <>
    <div>
      <h1>{light}</h1>
      <button onClick={() => {
        setLight(light === "ON" ? "OFF" : "ON")
          // light = light === "ON" ? "OFF" : "ON";
      }}
      >
        {light === "ON" ? "끄기" : "켜기"}
      </button>
    </div>
    </>
  )
}

export default App;

주석처리 된 라인들과 같이 변수를 선언할 경우 state 값이 변하지 않아 화면이 렌더링되지 않는다.

 

 

 

2. State와 Props

리액트 검포넌트에서 리레더링이 발생하는 상황

  1. 자신이 관리하는 state의 값이 변경되었을 때
  2. 자신이 제공받는 props의 값이 변경되었을 때
  3. 부모 컴포넌트가 리렌더링 되었을 때
import { useState } from "react";

const Bulb = ({light})=>{
  return(
    <div>
      {light === "ON" ? (
        <h1 style={{backgroundColor:"orange"}}>ON</h1>
        ) : (
        <h1 style={{backgroundColor:"gray"}}>OFF</h1>
        )}
    </div>
  );
}

function App() {
  const [count, setCount] = useState(0);
  const [light, setLight] = useState("OFF");

  return (
    <>
    <div>
      <h1>{count}</h1>
        <button
          onClick={() => {
          setCount(count + 1);
        }}
        >
          +
        </button>
    </div>
    <div>
      <Bulb light={light} />
      <button onClick={() => {
        setLight(light === "ON" ? "OFF" : "ON")
      }}>{light === "ON" ? "끄기" : "켜기"}</button>
    </div>
    </>
  )
}

export default App;

이런식으로 코드를 작성할 경우, count의 값만 증가시킬 때에도 부모 컴포넌트의 리렌더링으로 인해 Bulb와 같은 다른 자식 컴포너트들도 불필요하게 리렌더링이 된다.

이러한 자식 컴포넌트들이 많아질수록 성능이 저하되기 때문에 이를 방지해주는 것이 좋다.

따라서 관련 없는 두 개의 state를 하나의 컴포넌트에 몰아넣는 것 보다 서로 다른 컴포넌트로 분리해주는 것이 좋다. 각각 파일로 모듈화 해서 분리해주는 것도 좋다.

 

 

 

3. State로 사용자 입력 관리하기

// 간단한 회원가입 폼

import { useState } from "react";

const Register = () => {
    const [name, setName] = useState("이름");
    const [birth, setBirth] = useState("");
    const [country, setCountry] = useState("");
    const [bio,setBio] = useState("");

    const onChangeName = (e) => {
        setName(e.target.value);
    };
    const onChangeBirth = (e)=>{
        setBirth(e.target.value);
    };
    const onChangeCountry = (e)=>{
        setCountry(e.target.value);      
    };
    const onChangeBio = (e)=>{
        setBio(e.target.value);
    };

    return (
        <div>
            <div>
                <input
                value={name}
                onChange={onChangeName}
                placeholder={"이름"}
                />
            </div>

            <div>
                <input
                value={birth}
                onChange={onChangeBirth}
                type="date"
                />
            </div>

            <div>
                <select
                value={country}
                onChange={onChangeCountry}
                >
                <option value=""></option>
                <option value="kr">한국</option>
                <option value="us">미국</option>
                <option value="uk">영국</option>
                </select>
            </div>

            <div>
                <textarea
                value={bio}
                onChange={onChangeBio}
                />
            </div>
        </div>
    );
};

export default Register;
// 간단한 회원가입 폼

import { useState } from "react";

const Register = () => {
    const [input, setInput] = useState({
        name: "",
        birth: "",
        country: "",
        bio: ""
    });

    const onChange = (e) => {
        setInput({
            ...input,
            [e.target.name]: e.target.value
        })
    };


    return (
        <div>
            <div>
                <input
                name = "name"
                value={input.name}
                onChange={onChange}
                placeholder={"이름"}
                />
            </div>

            <div>
                <input
                name = "birth"
                value={input.birth}
                onChange={onChange}
                type="date"
                />
            </div>

            <div>
                <select
                name="country"
                value={input.country}
                onChange={onChange}
                >
                <option value=""></option>
                <option value="kr">한국</option>
                <option value="us">미국</option>
                <option value="uk">영국</option>
                </select>
            </div>

            <div>
                <textarea
                name="bio"
                value={input.bio}
                onChange={onChange}
                />
            </div>
        </div>
    );
};

export default Register;
여러 개의 state를 나눠서 관리하던 네 가지의 데이터를 객체 형태로 만들어서 하나의 스테이트로 함께 관리하는 방법으로 복잡한 state를 한 번에 관리할 때 편리하다.

태그

   -   input 태그: 입력값을 받는 태그

   -   textarea 태그: input 태그와는 달리 여러 줄을 입력 받을 수 있는 태그

속성

   -   placeholder: input에 아무런 값도 입력하지 않았을 때 회색으로 나오는 가이드 문구

   -   type="date": 이 경우 연도와 월, 일을 고를 수있는 date picker가 렌더링이 된다. (사용하고 있는 브라우저에 따라 조금씩 다른 모양으로 렌더링이 될 수 있다.)

  • 리액트에서 state를 이용해 사용자의 입력값을 저장하여 서버로 전달하거나 초기값을 설정하기 위해서 value 속성도 설정해야 한다. 
  •  ...input을 입력하여 기존의 input state에 들어있던 다른 관련없는 프로퍼티 값들을 그대로 유지시키도록 설정하여야 한다. 만약 이 라인을 빼먹을 경우, 객체로만 input state의 값이 변경되어 버리기 때문에 기존에 입력해 두었던 다른 property 값들이 사라지게 된다.
  • 문법 [e.target.name]: e.target.value은 새로운 객체를 만들면서 property의 키 자리에 대괄호를 열고 그 안에 변수의 이름을 넣으면 해당 변수의 값이 property의 키로써 설정이 된다.

4. useRef

useRef란 새로운 useReference의 줄임말로 객체를 생성하는 기능이다. 이를 활용해 생성한 이 레퍼런스 객체는 컴포넌트 내부의 변수로써 일반적인 값들을 저장할 수 있다. 또한 이를 이용하여 컴포넌트가 렌더링하는 특정 DOM 요소에 접근함으로써 해당 요소를 조작하는 것 (특정 요소에 포커스를 주거나 해당 요소의 스타일을 변경시키는 동작 등)도 가능하다. 

이벤트 핸들러가 계속 실행되어 변수가 변해도 컴포넌트를 리렌더링 하지 않기 때문에 이 reference object는 컴포넌트 내부에서 렌더링에 영향을 미치지 않아야 되는 변수를 생성할 때 활용한다.

useRef useState
컴포넌트 내부의 변수로 활용 가능
reference 객체를 생성 state를 생성
어떤 경우에도 리렌더링을 유발하지 않음 값이 변경되면 컴포넌트 렌더링

컴포넌트 내부에서 리렌더링을 유발하지 않는 변수를 만들고 싶다면 useRef라는 기능이 아니더라도 JavaScript 변수로도 할 수 있으나 변수 값이 정확하지 않을 수 있다.

인풋의 값을 입력하게 될 경우 이벤트 핸들러가 실행 되면서 state의 값을 변경해 컴포넌트가 리렌더링 된다. 즉, 컴포넌트 역할을 하는 함수가 다시 호출되기 때문에 함수 안에 있는 코드도 모두 다시 실행되어 변수를 초기화하는 라인도 다시 실행되어 계속 리셋된다. 

 

4.1. useRef 사용하기

1) 컴포넌트 내에서 const refObject = useRef()를 활용해 레퍼런스 객체를 생성한다

2) 태그에 ref={inputRef} 속성을 추가하여 해당 태그가 렌더링하는 DOM 요소를 reference object에 저장한다.

 

 

 

5. React Hooks

React Hooks란 클래스 컴포넌트에서만 이용할 수 있는 리액트의 특수한 기능들을 함수 컴포넌트에서도 사용할 수 있도록 도와주는 method이다.

2017년도 이전의 ReactJS
class 컴포넌트 function 컴포넌트
모든 기능을 이용할 수 있음 UI 렌더링만 할 수 있음
복잡한 문법 간결한 문법

두 컴포넌트들의 장점들을 활용하고자 React Hooks를 개발하였다.

  • react hook들은 함수 컴포넌트 내부에서만 호출될 수 있다.
  • 조건부로 호출되어서는 안 된다. 즉, 조건문이나 반복문에서는 호출이 불가능하다.
  • use라는 접두사를 이용해서 새로운 커스텀 훅을 만들 수 있다.
커스텀 훅 예시
import { useState } from "react";

function useInput() {
    const [input, setInput] = useState("");

    const onChange = (e) => {
        setInput(e.target.value);
    };

    return [input, onChange];
}

export default useInput;

커스텀 훅을 통해서 컴포넌트 내부에 반복되는 로직이나 훅을 사용하는 로직을 분리할 수 있다.

일반적으로, 커스텀 훅을 저장할 때에는 보통 src 디렉토리 아래에 hooks라는 별도의 폴더를 만들어서 hook의 이름으로 보관한다. 

'P&P 학회 > FRONT-END STUDY' 카테고리의 다른 글

7주차 (2024.11.18)  (1) 2024.11.17
6주차 (2024.11.11)  (1) 2024.11.08
5주차 (2024.11.04)  (0) 2024.10.30
3주차 (2024.10.14)  (0) 2024.10.09
2주차 (2024.10.07)  (4) 2024.10.08