React_03_State 상태 & Props 속성

송지윤·2024년 6월 4일

React

목록 보기
3/15

React State

상태 값을 의미하는 값이 변경되면 화면이 다시 렌더링 됨

React에서의 state란 컴포넌트 내부에서 관리되는 상태 값을 의미합니다.
컴포넌트가 생성되고, 갱신될 때마다 변경될 수 있는 값이며, 이 값이 변경될 때마다 화면이 다시 렌더링됩니다.
state는 useState를 사용하여 컴포넌트 내부에서 관리할 수 있으며, setState 함수를 통해 값을 업데이트할 수 있습니다.
React에서의 state는 컴포넌트의 상태를 저장하고, 필요에 따라 다시 렌더링하는 데 사용됩니다.

React Props

Props는 React 컴포넌트에게 데이터를 전달하는 방법 중 하나입니다.
Props는 부모 컴포넌트로부터 자식 컴포넌트로 전달되며, 컴포넌트 내부에서 변경할 수 없는 읽기 전용 데이터입니다.
-> userName 이라는 state 가 자식은 props 로 가져올 수 있지만 읽기만 할 수 있음 직접 변경할 수 없음

Props를 사용하면 컴포넌트 간의 데이터 전달이 간단하고 유지보수하기 쉬워집니다. 또한 컴포넌트의 재사용성을 높일 수 있습니다.

userName 부모 자식 Props props 안에 userName 이 들어있음

1. State lifting up : 상태 끌어올리기

React에서는 상위 컴포넌트가 하위 컴포넌트의 상태를 직접 변경할 수 없습니다.
따라서 React에서는 하위 컴포넌트에서 발생한 이벤트를 상위 컴포넌트에서 처리하도록 하는 상태 끌어올리기 패턴을 사용합니다.

Exam3.js

// State & Props

import { useState } from "react"

// State lifting up : 상태 끌어올리기
// 리액트에서는 부모 컴포넌트가 자식 컴포넌트의 상태를 직접 변경할 수 없다
// -> 자식에서 발생한 이벤트를 부모에서 처리하도록 하는 상태 끌어올리기 패턴을 이용해야한다.

// 해당 파일에서 컴포넌트 총 3개 존재
// Id 컴포넌트 (자식)
// Pw 컴포넌트 (자식)
// Exam3 컴포넌트 (부모) - 해당 파일에서 내보낼 기본 컴포넌트

// Id 컴포넌트 (자식)
// function Id() {

// }
// JS에서 사용하는 화살표 형태 함수도 사용 가능함
const Id = (props) => {
    // 부모가 전달한 함수를 받아야함 props : {onChangeId}
    // 부모에게서 전달받은 모든 속성을 props 라고 부름
    // 꺼내고 싶은 거 꺼내서 쓰면 됨
    // 꺼내는 방법
    const { onChangeId } = props; // props 안에 있는 onChangeId 라는 이름의 함수를 이용할 수 있게 됨.

    // const [id, setId] = useState(""); // 상태(state) 중 'id'를 생성하고 초기값 "" 설정

    return (
        // 함수형 컴포넌트는 render() 안 써도 됨 바로 return 쓰면 됨
        // JSX 문법 사용
        // JSX를 사용한 html 구문 작성부는 무조건 최상위 부모태그 하나로 감싸져 있어야한다.
        <> {/* 모두를 감싸는 최상위 태그 하나가 있어야함 빈태그여도 괜찮음 */}
            <div>
                <label>ID : </label>
                <input onChange={onChangeId}/>
                {/* onChange 이벤트 핸들러에 부모님한테 전달 받은 onChangeId를 연결해줄 거다. */}
            </div>
            {/* <h1></h1> // 이게 최상위 태그 */}
            {/* <h1></h1> // 같은 레벨의 형제가 있으면 에러남 */}
        </>
    );
}

// Pw 컴포넌트 (자식)
const Pw = ({onChangePw}) => {
    // props 대신 매개변수 자리에서 바로 부르는 방법
    // {} 를 이용하여 props 안에 있는 값을 바로 꺼내서 쓸 수도 있다.

    // const [pw, setPw] = useState(""); // 상태(state) 중 'pw'를 생성하고 초기값 "" 설정

    return (
        <>
            <div>
                <label>PW : </label>
                <input onChange={onChangePw}/>
                {/* 함수 연결 */}
            </div>
        </>
    );
}

// Exam3 컴포넌트 (부모)
const Exam3 = () => {
    // id 와 pw 가 여기에 정의 돼있어야 return 구문에서 사용할 수 있음
    // 상태 끌어올리기
    const [id, setId] = useState("")
    const [pw, setPw] = useState("");
    // 자식의 상태를 부모에서 정의 (상태 끌어올리기)

    // id, pw input 창 react 가 실시간으로 알 수 있는 이벤트 핸들러 만들어줘야함
    // -> input 창에 작성한 값이 id, pw 에 업데이트가 돼야함
    // input 창 안에 값이 작성될 때마다 상태를 업데이트 시키겠다는 eventHandler 필요

    // 자식의 상태를 변경할 수 있는 함수 정의
    // 상태 변경하려면 setId, setPw 이용하면 됨
    const onChangeId = (e) => {
        setId(e.target.value);
    }

    const onChangePw = (e) => {
        setPw(e.target.value);
    }

    // 위 함수를 이용하려면 호출해줘야함

    return (
        <>
            {/* 컴포넌트 중 Id를 불러 렌더링함 (Id가 Exam3의 자식이 됨) */}
            {/* <Id /> */}
            {/* -> 컴포넌트임, 태그 안에 onClick 이벤트를 사용할 수 없음 */}
            {/* 자식한테는 props 만할 수 있음 props 역할로서 전달만할 수 있음 전달할 때 이름 지정할 수 있음 */}
            {/* 짓고 싶은 이름으로 속성 작성 K:V 형태로 넘어감 */}
            <Id onChangeId={onChangeId}/>

            {/* 컴포넌트 중 Pw를 불러 렌더링함 (Pw가 Exam3의 자식이 됨) */}
            <Pw onChangePw={onChangePw}/>
            {/* onChangePw 이름으로 onChangePw 함수를 전달할거다. */}

            <div>
                {/* button 의 disabled 속성 : 비활성화 속성 (비활성 true / 활성 false)
                    -> id와 pw 둘 다 작성되어있어야 버튼 활성화 시켜줄 거
                */}
                <button disabled={id.length === 0 || pw.length === 0}>Login</button>
            </div>
        </>
    );
}

export default Exam3;

App.js

import logo from './logo.svg';
import './App.css';
import Exam1 from './exam/Exam1';
import Exam2 from './exam/Exam2';
import Exam3 from './exam/Exam3';

// 터미널 ctrl + shift + \` (백틱)으로 열고
// 터미널에서 yarn start 서버 켜기
// 터미널에서 ctrl + c 서버 끄기

function App() {
  return (
    <div className='App'>
      {/* 여러가지 컴포넌트 불러다 쓸 것임. ctrl + / 눌러서 자동완성 주석처리 */}
      {/* <Exam1 />
      <Exam2 /> */}
      <Exam3 />
      {/* div 에 className='App' 작성하면 가운데 정렬됨 */}
    </div>
  );
}

export default App;

버튼 활성화 됨

Props Drilling : 상태 내리꽂기

Props drilling은 React에서 사용되는 용어로, Props를 통해 데이터를 전달할 때, 하위 컴포넌트에서 필요하지 않은 Props를 계속해서 전달
하는 것을 의미합니다.
이러한 방법은 코드의 가독성을 떨어뜨리고, 유지보수를 어렵게 만들 수 있습니다.
따라서 Props drilling을 최소화하기 위해서는, 필요한 Props만을 전달하고, 필요하지 않은 Props는 하위 컴포넌트에서 직접 접근하는 것이
좋습니다.
이를 위해서는 React Context나 Redux와 같은 상태 관리 라이브러리를 사용하는 것도 좋은 방법입니다.

App 에 들어있는 상태 값이 3번째 줄 Child가 필요할 때
2번째 줄 Child 에서는 필요없지만 3번째 줄 에서 쓸려면 props를 전달해줘야함
하위 컴포넌트들이 끝도없이 내려감 누가 쓰는지 찾아가기 어려움
가독성 떨어뜨리고 유지보수 어렵게 만듦
=> React 에서 제공하는 Context, Redux 상태 관리 라이브러리가 있음
내용이 굉장히 방대함

비슷한 코드만 한번 써보기
Exam4.js

// State & Props

import { useState } from "react";

// Props Drilling : 상태 내리꽂기
// props를 통해서 데이터를 전달할 때, 자식 컴포넌트에서 필요하지 않은 props를 계속해서 전달하는 행위
// -> 코드의 가독성 및 유지보수성을 떨어뜨림
// -> React Context API 나 Redux 같은 상태관리 라이브러리를 사용함

// Exam4 > Child1 > Child2 > Child3 > MyComponent 이런 식으로 부모자식 관계가 연결되어 있을 때
// Exam4의 상태값을 MyComponent에서 사용해야 한다면?

// 부모 컴포넌트
function Exam4() {
    const [name, setName] = useState("홍길동");

    // handleClick 함수 정의
    const handleClick = () => {
        setName("빠삐리빠삐코");
    }

    return (
        <>
            {/* MyComponent 까지 전해주려면 Child1 불러서 name 상태값을 전달해줘야함 */}
            <Child1 name={name} />
            {/* name 이라는 키로 name 이라는 상태값 전달 */}
            <button onClick={handleClick}>이름 변경</button>
        </>
    );
}

// Child1 함수 정의
// 부모에게서 호출되고 있고 name 이라는 이름으로 props 전달됨
// props 전달 받을 준비
function Child1(props) {
    // 하나의 태그만 return 할 때는 소괄호 생략 가능
    return <Child2 name={props.name} />
    // name 이라는 키로 props 안에 name 을 전달하겠다.
}

// Child2 함수 정의
function Child2(props) {
    return <Child3 name={props.name} />
}

// Child3 함수 정의
function Child3(props) {
    return <MyComponent name={props.name} />
}

// 최종적으로 MyCompoment 에서 name 사용하 것
function MyComponent(props) {
    // props 는 Exam4 에서부터 내리꽂기를 통해 전달받은 무보의 상태값

    return <h1>{props.name}</h1>
}

export default Exam4;

App.js

import logo from './logo.svg';
import './App.css';
import Exam1 from './exam/Exam1';
import Exam2 from './exam/Exam2';
import Exam3 from './exam/Exam3';
import Exam4 from './exam/Exam4';

// 터미널 ctrl + shift + \` (백틱)으로 열고
// 터미널에서 yarn start 서버 켜기
// 터미널에서 ctrl + c 서버 끄기

function App() {
  return (
    <div className='App'>
      {/* 여러가지 컴포넌트 불러다 쓸 것임. ctrl + / 눌러서 자동완성 주석처리 */}
      {/* <Exam1 />
      <Exam2 /> */}
      {/* <Exam3 /> */}
      <Exam4 />
      {/* div 에 className='App' 작성하면 가운데 정렬됨 */}
    </div>
  );
}

export default App;

홍길동이라는 이름은 MyComponent 에서 불러와 진 거

Redux 굉장히 큰 라이브러리 큰 프로젝트에서는 Redux 도입해서 사용
React Context API -> 어느 정도 사용 가능함

Props 응용 전개 연산자

Exam5.js

// props 응용
// 전개 연산자 (...) : 객체 내부의 모든 값을 props로 전달하는 방법

import { useState } from "react";

// 부모
function Exam5() {
    const [userData, setUserData] = useState({ name : '홍길동' , age : 30 });
    // 자바스크립트 객체 형태 userData 안에 상태값으로 넣겠다.

    // return <MyComponent name={userData.name} age={userData.age}/>
    // 이렇게 사용해도 되지만 한번에 값을 넘겨주고 싶을 때 전개연산자 사용
    return <MyComponent {...userData} />
    // 뭉쳐있는 걸 전개연산자 이용해서 펼쳐줌
}

// 자식
function MyComponent(props) {
    const {name, age} = props;
    // 자식 컴포넌트도 props 안에 name 값 age 값 꺼내서 사용할 수 있음

    const content = `안녕하세요 제 이름은 ${name} 이고, 나이는 ${age} 세 입니다.`;
    return <h3>{content}</h3>
}

export default Exam5;

App.js

import logo from './logo.svg';
import './App.css';
import Exam1 from './exam/Exam1';
import Exam2 from './exam/Exam2';
import Exam3 from './exam/Exam3';
import Exam4 from './exam/Exam4';
import Exam5 from './exam/Exam5';

// 터미널 ctrl + shift + \` (백틱)으로 열고
// 터미널에서 yarn start 서버 켜기
// 터미널에서 ctrl + c 서버 끄기

function App() {
  return (
    <div className='App'>
      {/* 여러가지 컴포넌트 불러다 쓸 것임. ctrl + / 눌러서 자동완성 주석처리 */}
      {/* <Exam1 />
      <Exam2 /> */}
      {/* <Exam3 /> */}
      {/* <Exam4 /> */}
      <Exam5 />
      {/* div 에 className='App' 작성하면 가운데 정렬됨 */}
    </div>
  );
}

export default App;

출력 화면

0개의 댓글