코딩 공부/React.js 리액트

[4주차 - 2] React.js 입문

recordmastd 2024. 11. 5. 23:41

State(=상태)

: 현재 가지고 있는 형태나 모양을 정의, 변화할 수 있는 동적인 값
컴포넌트의 현재 상태를 보관하는 변수

- state의 값에 따라 다른 UI를 렌더링
- state의 값의 변화를 감지하여 자동으로 컴포넌트 렌더링 (=리렌더, 리렌더링)

//App.jsx파일

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

function App() {
  const [count, setState] = useState(0);    // 비 구조화 할당 문법(=구조분해할당)사용
  const [light, setLight] = useState("OFF");

  // 버튼 클릭 시 state의 값이 1 증가(=클릭 시 리렌더링 발생)
  return (
    <> 
      <div>
        <button onClick={() => {
          setLight(light === "ON" ? "OFF" : "ON");
         }}
        >
          {light === "ON" ? "끄기" : "켜기"}
        </button>
      </div>
      <div>
        <h1>{count}</h1>
        <button 
           onClick={() => {
            setState(count + 1);
         }}
        > 
          +
        </button>
      </div>
    </>
  );
};

export default App;

- useState의 첫번째 요소는  새롭게 생성된 state 값, 두번째 요소는 state의 값을 변경하는 함수(=상태변화함수)

- 클릭할 때마다 리렌더링 발생
컴포넌트 state값 변경>컴포넌트 리턴>화면 다시 렌더링>변경된 state의 값 화면에 반영

- 일반변수가 아닌 state를 사용해야 리렌더링이 발생함 (컴포넌트의 상태이기 때문)


 State와 Props

/ 전구 컴포넌트 생성
const Bulb = ({light}) => {
  return (
    <div>
      {light === "ON" ? (<h1 style={{backgroundColor: "orange" }}>ON</h1>) 
      : (<h1 style={{backgroundColor: "gray"}}>OFF</h1>)}
    </div>
  )
}
 
function App() {
  const [count, setState] = useState(0);
  const [light, setLight] = useState("OFF");

  // 버튼 클릭 시 state의 값이 1 증가
  return (
    <> 
      <div>
        <Bulb light={light} />
        <button onClick={() => {
          setLight(light === "ON" ? "OFF" : "ON");
         }}
        >
          {light === "ON" ? "끄기" : "켜기"}
        </button>
      </div>
      <div>
        <h1>{count}</h1>
        <button 
           onClick={() => {
            setState(count + 1);
         }}
        > 
          +
        </button>
      </div>
    </>
  );
};

- Bulb를 자식 컴포넌트로 넣은 후 light 스테이트를 설정해준다

- state값이 변경되지 않아도 props가 변하면 리렌더링 된다(ON/OFF에 따라 바뀌는 페이지)

light의 state가 ON/OFF 상태로 있을 수 있고,
Bulb 컴포넌트에게 props(부모가 자식에게 넘겨주는 데이터)인 light값을 넘겨준다

 

리액트에서 리렌더링 되는 경우 3가지
1. state
2. props
3. 부모 컴포넌트

효율적 리렌더링을 위한 컴포넌트 분리

Bulb컴포넌트

const Bulb = () => {
  const [light, setLight] = useState("OFF");

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

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

Counter컴포넌트

const Counter=() => {
  const [count, setState] = useState(0);
  
  return (
    <div>
      <h1>{count}</h1>
      <button 
        onClick={() => {
        setState(count + 1);
      }}
    > 
       +
      </button>
   </div>
  );
};

App

function App() {
  return (
    <> 
      <Bulb/ >
      <Counter/ >
    </>
  );
};

- components 폴더 내에 컴포넌트명.jsx 파일을 생성하여 분리하는 편이 더 효율적이다

import/export문 잊지 않기


State로 사용자 입력 관리하기 - 간단한 회원가입 폼 만들기

Register.jsx 파일 생성

사용자의 정보를 state를 통해 저장

 

1. 이름 입력

// 간단한 회원가입 폼
import { useState } from "react";

const Register = () => {
    const [name, setName] = useState("이름");   // "이름"을 초기값으로 설정

    const onChangeName = (e) => {
        setName(e.target.value);    // value값 가져와서 name으로 설정
    };

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

export default Register;

 

- placeholder: 인풋창에 작성가이드처럼 있는 투명글자

- 초기값 설정을 위해 value 속성도 설정한다

- console.log(e)로 value값을 알아내기

...을 클릭하면 value값이 나옴

- 알아낸 value값을 setName의 매개변수로 넣어서 name값으로 지정한다

 

2. 날짜 입력

- div로 나눠서 날짜창을 줄바꿈을 한다

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

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

- name과 똑같이 useState, 이벤트핸들러 등을 만들면 된다

- 날짜 입력을 위한 코드 작성시 오류가 발생하지만 불가피하므로 넘어가준다

 

국가 선택

- state와 이벤트핸들러는 동일하게 작성

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

 - 셀렉트박스의 맨 처음 옵션이 초기값으로 설정된다
-빈옵션칸을 만들면 초기값으로 아무것도 설정되지 않는다

- 셀렉트박스의 선택지는 친절하게 설정, value값은 더 간단하게 따로 설정하는 것이 일반적이다
(value = "kr", option값은 한국)

 

자기소개 작성

        <div>
            <textarea value={""} onChange={onChangeBio}></textarea>
        </div>

- textarea는  input과는 다르게 여러줄을 작성할 수 있다(내부동작은 동일)


더 효율적인 작성법

useState문이나 onChange가 반복되는 코드를 통합한다

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

- 객체 형태를 통해 useState문을 통합

- 값들을 유지시키기 위해 ..input 스프레드 연산자를 통해 모든 값을 가져온 후 필요한 타겟값만 변경해준다

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

 

<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}></textarea>
        </div>
    </div>
[e.target.value.name]: e.target.value

- 이벤트 핸들러가 동작하는 state가 e.target.value.name가 되므로 동작에 따라 key값이 설정된다

(자기소개서를 작성하는 이벤트가 있으면 bio: e.target.value와 동일함)


useRef

컴포넌트 내부의 변수로써 일반적인 값 저장 가능

    const refObj = useRef();    // current: undefined 출력

- currnet라는 프로퍼티에 현재 보관값을 저장

 

클릭 수  ref의 current에 저장하여 알아보기

콘솔창에서 crrunt값이 1씩 증가함, 이벤트 핸들러만 실행되고 리렌더링X

제출 시 이벤트 항목에 포커스 만들기

    const inputRef = useRef();  // input 요소가 저장하는 DOM요소 저장됨    
    
    // 이벤트 핸들러 설정
    const onSubmit = () => {
        if(input.name === ""){
            inputRef.current.focus();
        } 
    }
    
    <div>
            <input
            ref = {inputRef}
            name="name"
            value={input.name}
            onChange={onChange}
            placeholder={"이름"}
             />
        </div>
        
        <button onClick={onSubmit}>제출</button>

 

- 일반변수는 리렌더링시 초기화되지만, state와 useRef는 초기화되지 않는다
- - 만약 컴포넌트 외부에 일반변수를 둔다면 다른 컴포넌트를 공유하기 때문에 심각한 문제가 발생한다


React Hooks

: 클래스 컴포넌트의 기능을 함수 컴포넌트에서도 이용할 수 있도록 하는 메서드

- 과거 class 컴포넌트(기능많음)사용 시절 불편함을 해소하기 위해 function(기능적음) 컴포넌트 사용을 기대했고,

 class컴포넌트 기능을 function 컴포넌트에서도 사용하기 위해 react hooks으로 기능을 가져왔다

 그 결과 현재 function 컴포넌트만 사용한다

- 메서드 이름 앞에 use가 붙는다 (예: useSTate, useRef)

 

- 함수 컴포넌트, 훅 메서드 내부에서만 호출 가능
- 조건문, 반복문에서는 사용 불가
- 자신만의 react hook을 만들 수 있다 (앞에 use 사용 필요)

커스텀 훅 예시
반복사용가능

- hooks 폴더 내에 분리하는 것이 일반적이다