Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

heyday2024 님의 블로그

[React 2주차 (3)] 컴포넌트와 JSX, props, state 본문

프론트엔드 부트캠프

[React 2주차 (3)] 컴포넌트와 JSX, props, state

heyday2024 2024. 10. 30. 13:02

React Components

컴포넌트를 통해 UI를 재사용이 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있음. 개념적으로 컴포넌트는 JavaScript 함수와 유사함. “props”(properties에서 따옴)라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환함

 

<리액트 컴포넌트 두가지 표현법>

1. 함수형

// props라는 입력을 받음
// 화면에 어떻게 표현되는지를 기술하는 React 엘리먼츠를 반환(return)

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 훨씬 쉬운 표현을 해보면 아래와 같죠.
function App () {
	return <div>hello</div>
}

--->  입력을 받아도 되고 안 받아도 됨.

==> 주로 권장되는 방식. (함수형 컴포넌)

 

2. 클래스형

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

 

 

 

<컴포넌트 영역>

컴포넌트(함수 형태) 영역

1. 컴포넌트 밖에서 필요한 파일 가져옴 

2. 자바스크립트 쓰는 영역

3. return으로 html과 유사한 형태의 요소(JSX) 반환...

 4. export 로 내가 만든 컴포넌트 밖으로 보내기

 

 

===> 폴더이름은 camelCase, 컴포넌트 만드는 파일(jsx)이름은 PascalCase 로 만들기

 

React에 존재하는 모든 UI적 요소는 component이고,  각 component는 부모 자식 관계가 있을 수 있음.

 

 

<컴포넌트 만들기 예시>

import React from "react";
function App() {
  // <---- 자바스크립트 영역 ---->
  const handleClick = () => alert("안녕!");

  return (
    /* <---- HTML/JSX 영역  ---->*/
    <div
      style={{
        height: "100vh",
        display: " flex",
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      {/* 이곳에 퀴즈를 위한 html 코드를 작성해 주세요 */}
      <span>이것은 내가 만든 앱 컴포넌트임.</span>
      <button onClick={handleClick}>클릭!</button>
    </div>
  );
}

export default App;

 

- import React from "react";는 React 라이브러리를 가져와 컴포넌트 작성에 필요한 기능을 사용하기 위해 필요함.

- 과거에는 JSX 문법을 사용할 때 React 라이브러리를 반드시 가져와야 했지만, 현재는 새로운 버전의 React(17+)에서는 React를 직접 가져오지 않아도 JSX를 사용할 수 있음.

===> 즉, 최신 버전의 React에서는 JSX가 자동으로 React를 참조하여 import React from "react";가 꼭 필요하지는 않음.

 

** JS 문법을 적용하려면 {} 중괄호 꼭 붙여주기!!!

 


 

부모 - 자식 컴포넌트

컴포넌트는 다른 컴포넌트를 품을 수 있음.

이때 다른 컴포넌트를 품는 컴포넌트 === 부모 컴포넌트

다른 컴포넌트 안에서 품어지는 컴포넌트 === 자식 컴포넌트

 

function App() {
  return (
    <div>
      <h1>부모-자식 관계</h1>
      <GrandFather></GrandFather>
    </div>
  );
}

export default App;

function GrandFather() {
  return <div>
    <p>나야, 할아버지</p>
    <Mother></Mother>
  </div>;
}

function Mother() {
  return <div>
    <p>나야, 엄마</p>
    <Child></Child>
  </div>;
}

function Child() {
  return <div>
    <p>나야, 자식</p>
  </div>
}

- export default로 App을 내보내어 브라우저에서 렌더링할 수 있게함.

위 코드를 실행시켜서 나오는 결과(렌더링된 것)

 

** VS코드 확장 simple react, ES7+ 써서 코드 스니펫 사용함. rafce 치면 자동으로 틀 짜줌.


JSX (Javascript XML)

React에서 UI를 정의할 때 사용하는 JavaScript 확장 문법임. HTML과 유사한 구문을 사용하여 구조를 표현할 수 있게 해줌으로써, JavaScript 코드 안에 직접 UI 구성을 작성할 수 있게 해줌. 브라우저가 직접 JSX 파일을 이해할 수는 없지, Babel 같은 컴파일러가 JSX를 순수 JavaScript 코드로 변환함으로써 브라우저에서 실행될 수 있음.

// JavaScript를 확장한 문법
// JavaScript의 모든 기능이 포함되어 있으며, React Element를 생성하기 위한 문법
const element = <h1>Hello, world!</h1>;

 

==> 원래 html은 js 파일 내에서 쓸 수 없었는데 이를 가능하게 만든 것! JS 안에서 html태그같은 마크업을 넣어 UI(뷰작업) 작업을 편하게 할 수 있음!

 

 그럼 JSX에서 쓰는 <div>~~</div>는 DOM 요소인가요?

정확히는 React 요소!!!

 

즉, JSX에서 작성하는 요소들은 실제 DOM 요소가 아니라, React가 가상 DOM(Virtual DOM)에 구성하는 React 요소로 해석됨.

가상돔 ==> React에서 UI 업데이트 성능을 높이기 위해 사용하는 개념으로, 실제 DOM의 변경 작업을 최적화하기 위해 만들어진 메모리 내의 가벼운 DOM 복사본임. React는 이 가상 DOM을 통해 UI 상태 변화를 빠르게 반영하고, 필요한 부분만 실제 DOM에 업데이트함으로써 성능을 높임.

 


<JSX 쓸때 주의>

  • 태그 꼭 닫아주기 
  • 무조건 한개의 element만 반환하기(굳이 여러개를 감싸는 태그를 안 만들고 싶으면 빈태그라도 감싸줘야함)
  • html 쓸떄 썼던 class 대신 className 사용하기
  • 인라인으로 디자인적 요소 넣고 싶으면 js 문법이니까 중괄호 안에 넣고 싶은 스타일을 객체 형식으로 넣어줌(객체도 중괄호에 감싸있으니까 중괄호가 중첩된 상태.)
// 중괄호를 두 번 쓰는 이유? 객체도 자바스크립트니까요!
// 이렇게 쓰거나,
<p style={{color: 'orange', fontSize: '20px'}}>orange</p>

//혹은 스타일 객체를 변수로 만들고 쓸 수 있어요!
function App() {
  const styles = {
    color: 'orange',
    fontSize: '20px'
  };

  return (
    <div className="App">
      <p style={styles}>orange</p>
    </div>
  );
}

Props

부모 ---> 자식 데이터 전달 매커니즘.

 

<정보 교환 방식>

props === 부모 컴포넌트가 자식 컴포넌트에게 물려준 데이터!

props를 사용해서 컴포넌트 간의 정보를 교류할 수 있음.

  1. props는 반드시 위에서 아래 방향으로 흐른다. 즉, [부모] → [자식] 방향으로만 흐른다(단방향).
  2. props는 반드시 읽기 전용으로 취급하며, 변경하지 않는다.
// src/App.jsx

import React from "react";

function App() {
  return <GrandFather />;
}

function GrandFather() {
  return <Mother />;
}

function Mother() {
	const name = '홍부인';
  return <Child motherName={name} />; // 💡"props로 name을 전달했다."
}

function Child(props) {
  const name = props.name;
  return <div>{name} 연결 성공</div>; //props 이용해서 name 받기
}



export default App;

** props는 object literal (객체)이기 때문에 props 안에 있는 여러 데이터들 중 하나를 . notation으로 가져와 쓸 수 있음.

부모가 props를 자식에게 넘겨줄 떄 key값을 정해줄 수 있음.

--> object literal 이란 {key:"value"}의 데이터 형태를 의미

 

즉, props === 부모 컴퍼넌트가 자식에게 넘겨준 데이터들의 묶음

이런 식으로 리액트에서는 Props를 통해 부모 컴퍼넌트로부터 자식 컴포넌트에 데이터를 넘겨줄 수 있음.

===.> 자식 컴포넌트에서는 부모 컴포넌트로 props 전달 못함!!!!!!!!!

 


참고로 prop-types 관련 오류생겼을 때,

.eslintrc 파일에 규칙 추가하기.

{
  "rules": {
    "react/prop-types": "off"
  }
}

- ESLint는 JavaScript와 TypeScript 코드의 오류를 자동으로 감지하고 코드 스타일을 강제하는 정적 코드 분석 도구로, 코드가 실행되기 전에 잠재적 오류나 일관성 없는 코드 스타일을 찾아내어 코드 품질을 높이는 데 도움을 줌.

 


<prop drilling>

[부모] → [자식] 컴포넌트간 데이터 전달이 이루어지는 방법이 props임.

[부모] → [자식] → [그 자식] → [그 자식의 자식] 이 데이터를 받기 위해선 무려 3번이나 데이터를 내려줘야함.

==> 이걸 바로 prop drilling, props가 아래로 뚫고 내려간다. 라고 함.

export default function App() {
  return (
    <div className="App">
      <FirstComponent content="Who needs me?" />
    </div>
  );
}

function FirstComponent({ content }) {
  return (
    <div>
      <h3>I am the first component</h3>;
      <SecondComponent content={content} />|
    </div>
  );
}

function SecondComponent({ content }) {
  return (
    <div>
      <h3>I am the second component</h3>;
      <ThirdComponent content={content} />
    </div>
  );
}

function ThirdComponent({ content }) {
  return (
    <div>
      <h3>I am the third component</h3>;
      <ComponentNeedingProps content={content} />
    </div>
  );
}

function ComponentNeedingProps({ content }) {
  return <h3>{content}</h3>;
}

 

Prop Drilling은 부모 컴포넌트의 데이터를 자식 컴포넌트로 전달할 때, 여러 중간 컴포넌트를 거쳐 props로 전달하는 방식임. 작은 규모의 프로젝트에서는 간단히 적용할 수 있지만, 트리가 깊어질수록 관리가 어려워짐.

 

Redux는 전역 상태를 관리하기 위한 라이브러리임. 하나의 중앙 저장소(Store)를 통해 상태를 관리하며, 컴포넌트 간 상태 공유가 쉬워짐으로써 prop drilling 문제를 해결함. Store에 상태가 저장되고, Action으로 상태 변경 요청이 들어오면 Reducer가 새로운 상태를 반환함.

 

<Props Children>

 

리액트에서 컴포넌트의 자식 요소를 전달하고 표시할 때 사용하는 특수한 props임(즉, children도 props 맞음). 부모 컴포넌트가 자식 컴포넌트를 포함할 때, 해당 자식 요소를 props.children을 통해 접근하고 렌더링할 수 있음.

 

// src/App.jsx

import React from "react";

function User() {
  return <div></div>;
}

function App() {
  return <User>안녕하세요</User>;
}
export default App;

- App 컴포넌트에서 '안녕하세요'라는 정보를 보냈지만, User에서 아직 그 컴포넌트를 받지 못한 상태. 하지만, 이런 식으로 정보를 보내는 방식이 children props를 보내는 방식임. 

 

function User(props) {
	console.log(props.children)
  return <div></div>;
}

- 기존과 동일하게 children이라는 이름으로 정보 받으면 됨.

 

<children 용도>

Layout 컴포넌트를 만들 때 주로 사용

import React from "react";

const App = () => {
  return <div>
    <Layout>여긴앱입니다.</Layout>
  </div>;
};

//구조분해 할당
const Layout = ({children}) => {
  // const {children} = props
  return <main>
    <header>헤더 입니다</header>
    {children}
    <footer>푸터 입니다</footer>
  </main>;
};

export default App;

- 공통으로 들어가는 헤더와 푸터 컴포넌트를 한번만 작성해도 모두 보여지게 할 수 있음.

 

 

정리하자면, 

 

< props 주는 법 >

1. 명시적으로 props 선언하여 전달

컴포넌트에 속성(key)과 값을 직접 지정해 props를 전달할 수 있음. 이렇게 하면 지정된 이름(key)을 통해 원하는 값을 참조 가능함.

 

2. 태그 사이에 값 넣기 (자동으로 children으로 매칭됨)

태그 사이에 값을 넣으면 props.children으로 접근할 수 있음. 이 방식은 자식 요소를 직접적으로 감싸는 컴포넌트에 유용함.

 


<구조분해할당과 props>

function Todo(props){
	return <div>{props.todo}</div>
}

- 이런식으로 적으면 앞으로 많은 양의 데이터들을 각각 불러올 때 번거롭게 props를 계속 붙여주어야함

 

구조분해할당을 이용하면 훨씬 간단하게 작성 가능

function Todo({ title, body, isDone, id }){
	return <div>{title}</div>
}

 

<default arguments>

부모 컴포넌트에서 자식 컴포넌트로 정보 전달 시 사용함.

특정 props가 부모 컴포넌트로부터 전달되지 않을 수 있는데, 이 경우 자식 컴포넌트에서 해당 props의 기본값을 설정해주어야함.

function Welcome({ name = "Guest" }) {
    return <h1>Welcome, {name}!</h1>;
}

- name이라는 prop이 부모요소로부터 전달되지 않았을 때, Guest를 사용하도록 설정함.


State

컴포넌트 내부에서 바뀔 수 있는 값을 의미.

==> React 컴포넌트에서 동적인 데이터를 관리하고 렌더링에 반영할 수 있도록 해주는 객체임

===> 사용자가 입력한 값이나 버튼 클릭과 같은 상호작용에 따라 변화하는 정보를 담음.

 

  • 컴포넌트의 고유 상태:
    • 각 컴포넌트는 독립적인 상태를 가질 수 있으며, 이 상태는 컴포넌트 안에서만 관리됨.
  • 변화에 따라 UI 업데이트:
    • State가 변경되면 React는 변경 사항을 감지하고 해당 컴포넌트를 다시 렌더링하여 새로운 UI를 반영함.
  • 불변성:
    • State는 직접 수정하지 않음. 상태를 업데이트할 때는 항상 새로운 값을 할당함으로써 기존 State를 불변 상태로 유지함.

 

State를 만들 때는 useState()를 사용한다.

 

useState()는 React의 훅(Hook) 중 하나로, 함수형 컴포넌트에서 상태(state)를 관리할 수 있도록 해줌.

==>React의 기본 상태 관리 방식으로, 컴포넌트가 재렌더링될 때 상태를 보존하고 변경할 수 있는 기능을 제공함.

 

useState의 사용법과 동작 원리

useState는 두 가지 값을 가진 배열을 반환함:

const [state, setState] = useState(initialValue);
  1. 현재 상태 값 (state)
  2. 상태를 업데이트할 수 있는 함수 (setState)

useState은 state을 만들어주는 리액트에서 제공하는 기능으로, 이것을 훅(hook)이라고 부름!

 

훅(Hook)이란?

은 React 16.8 버전에서 도입된 기능으로, 함수형 컴포넌트에서도 상태 관리와 라이프사이클 기능을 사용할 수 있도록 해주는 도구임.

주요 특징

  • 함수형 컴포넌트에서 상태와 기능을 추가 가능
  • 코드의 재사용성과 가독성을 높여줌
  • 클래스형 컴포넌트에서만 사용하던 라이프사이클 메서드의 기능을 함수형 컴포넌트로도 구현 가능

주요 훅

  1. useState: 상태 관리
  2. useEffect: 컴포넌트의 라이프사이클 관리
  3. useContext: 컨텍스트 API를 활용한 전역 상태 관리

<useState 사용해서 state 변경하기>

// src/App.js

import React, { useState } from "react";

function Child(props) {
  return (
    <div>
      <button
        onClick={() => {
					props.setName("박할아"); // 드디어 받은 setName을 실행합니다.
        }}
      >
        할아버지 이름 바꾸기
      </button>
      <div>{props.grandFatherName}</div>
    </div>
  );
}

function Mother(props) {
  return (
    <Child grandFatherName={props.grandFatherName} setName={props.setName} /> // 받아서 다시 주고
  );
}

function GrandFather() {
  const [name, setName] = useState("김할아");
  return <Mother grandFatherName={name} setName={setName} />; // 주고
}

function App() {
  return <GrandFather />;
}

export default App;

 

-> state 변경 시 setValue(바꾸고 싶은 값) 사용

===>위 코드에서 setName통해서 바꾼 값은 어디에 저장되는 것이 아니기 때문에 단순히 화면에서만 바뀐 값으로 다시 렌더링이 됨.


<DOM handler event + state>

1. userState + onClick event

 

버튼을 눌렀을 때 하고 싶은 행동을 함수로 만들 수 있습니다.

아래 코드와 같이 onClickHandler 라는 함수를 만들고 onClick={} 에 넣어주었습니다. React에서는 이러한 방식으로 함수와 컴포넌트(button 태그)를 연결킵니다.

 

import React, { useState } from "react";

function App() {
  const [name, setName] = useState("길동이");

  function onClickHandler() {
    setName("누렁이");
  }

  return (
    <div>
      {name}
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

 

2. useState + onChange event

import React, { useState } from "react";

const App = () => {
  const [value, setValue] = useState("");

  const onChangeHandler = (event) => {
    const inputValue = event.target.value;
    setValue(inputValue);
    console.log(inputValue);
    
  };

	console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?

  return (
    <div>
      <input type="text" onChange={onChangeHandler} value={value} />
    </div>
  );
};

export default App;

- 사용자가 입력한 input 값 event.target.value로 꺼내 사용 가능.

- 입력하는 도중 계속, 그 입력이라는 이벤트가 진행될 때마다 input값을 받는 것임.

- state인 value를 input의 attribute인 value에 넣어주면 input과 state 연결됨.

 


제어 컴포넌트(Controlled Component) VS 비제어 컴포넌트(Uncontrolled Component)

 

1. 제어 컴포넌트 (Controlled Component)

  • 정의: 제어 컴포넌트는 컴포넌트의 상태가 React의 상태(state)로 관리되는 컴포넌트임.
  • 상태 관리: 폼 요소의 값은 항상 React의 상태에 의해 결정되고, 입력값이 변경될 때마다 상태가 업데이트됨.
  • 특징:
    • input의 value 속성을 React 상태와 연결하고, 사용자의 입력이 있을 때마다 onChange 이벤트를 통해 상태를 업데이트함. (===> 일반적인 방식!!)
    • 상태가 소스 오브 트루스(source of truth) 역할을 하여 입력 필드의 값이 항상 React 상태와 동기화됨.
      • source of truth: 데이터의 정확하고 최신 상태를 유지하는 주된 위치나 저장소를 가리킴. 즉, 특정 정보가 어디서 가장 정확하고 신뢰할 수 있는지를 나타냄. 예시에서 리액트의 state이 input의 value와 엮여있으므로 state 자체가 input의 변경된 정보 자체를 가리킴.
  • 일관성: React의 상태와 UI가 항상 일치하므로, 데이터 흐름이 일관됨.
import React, { useState } from 'react';

const ControlledComponent = () => {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (e) => {
    setInputValue(e.target.value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={handleChange} />
      <p>입력한 값: {inputValue}</p>
    </div>
  );
};

export default ControlledComponent;

--> input 요소의 value 속성과 onChange 이벤트를 함께 사용하여 입력 필드의 상태를 관리하는 것

 

2. 비제어 컴포넌트 (Uncontrolled Component)

  • 정의: 비제어 컴포넌트는 React의 상태로 관리하지 않고, DOM 요소 자체에서 직접적으로 관리하는 컴포넌트임.
  • 특징:
    • input의 value 속성을 React 상태와 연결하지 않고, 대신 onChange 이벤트를 통해 입력값을 감지하여 상태를 업데이트함. ==> 이렇게 하면 사용자의 입력에 대해 반응할 수 있지만, 입력 필드 값은 React 상태에 의해 제어되지 않음.
    • 입력 필드는 자신의 내부 상태를 유지하며, React의 상태와 동기화되지 않음.
  • DOM 접근: React의 상태에 종속되지 않으므로, ref를 통해 값을 가져오고, 외부 라이브러리와 통합하는 데 유리할 수 있음.
import React, { useState } from "react";

const App = () => {
  const [inputValue, setInputValue] = useState("");

  const onChangeHandler = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <input type="text" onChange={onChangeHandler} />
      <p>입력값: {inputValue}</p> {/* 입력값을 화면에 표시 */}
    </div>
  );
};

export default App;

- 상태 업데이트 지연: setState (여기서는 setInputValue) 호출 후 상태가 즉시 업데이트되지 않습니다. React 상태 업데이트는 비동기적으로 처리되기 때문에 onChangeHandler 내에서 console.log(inputValue)를 호출하면 변경된 최신 값이 아닌 이전 값을 출력해요. 물론 나중에 useEffect 훅을 배우면 state의 변경에 따른 값을 tracking 할 수 있습니다.

 

 

==> 일반적으로 폼 데이터를 더 명확하게 제어하고 싶다면 제어 컴포넌트 방식을 사용하는 것이 좋습니다. 그렇게 해야 입력 필드의 값이 항상 예측 가능하고 관리하기 쉬운 상태로 유지할 수 있어요!


 

불변성

불변성이란 메모리에 있는 값을 변경할 수 없는 것

자바스크립트의 데이터 형태중에 원시 데이터는 불변성이 있고, 원시 데이터가 아닌 객체, 배열, 함수는 불변성 없음.

<변수저장 방식 reminder(원시형, 참조형)>

let a = 1;
let b = 1;
console.log(a === b)

- 원시형: 값은 값을 할당 받으면, 저장된 값의 메모리주소를 두 식별자가 참조함. 그래서 둘이 같은지 비교하면 true가 나옴.- 즉, 변수의 이름은 다르지만, 같은 메모리 값을 바라보고 있음.

 

===> 데이터 수정 시, 만약, b = 2; 라고 재할당을 해주면, 기존 메모리에 저장이 되어 있는 1이라는 값이 변하지 않고, 새로운 메모리 저장공간에 2가 생기고 그 값을 참조하게 됨. 즉, 1 이라는 값 자체는 참조되지 않을 뿐, 그대로 남아있음(불변) --> 물론 나중에 참조가 아예 안되면 가비지 컬렉터에 의해 사라질 예정.

 

 

let obj1 = {name: 'lee'};
let obj2 = {name: 'lee'};

console.log(obj1 === obj2); //false

- 참조형: 같은 값을 할당 받아도 따로 선언이 되면 두 객체는 동등연산자로 비교시 false가 나옴. 그 이유는 객체와 같은 큰 데이터들을 갖는 참조형은 각각이 저장될 별도의 메모리 공간을 부여받기 때문. 

 

==> obj1을 수정한다고 하면, 객체는 불변성이 없기 때문에, 기존 메모리 저장공간에 있는 {name: 'lee'}값 자체가 바뀜. 즉, 참조형 데이터느 수정했을 때 기존에 저장되어있던 값 자체를 바꿈.

 

 

정리: 원시데이터는 수정을 했을 때 메모리에 저장된 값 자체는 바꿀 수 없고, 새로운 메모리 저장공간에 새로운 값을 저장합니다. 원시데이터가 아닌 데이터는 수정했을 때 기존에 저장되어 있던 메모리 저장공간의 값 자체를 바꿔버립니다.

 

 

불변성을 깨뜨리는 것이 왜 문제가 될까요?

  1. 예측 불가능한 코드: 데이터 구조를 직접 변경하면, 프로그램의 다른 부분에서 해당 데이터 구조를 참조하고 있을 때 그 부분들도 예상치 못한 방식으로 변경될 수 있습니다. 따라서 알 수 없는 버그를 발생시킬 수 있고, 프로그램의 동작을 예측하기 어렵게 만듭니다.
  2. 버그 추적의 어려움: 원본 데이터가 여러 곳에서 변경될 수 있다면, 어떤 부분의 코드가 데이터를 변경했는지 추적하기 어려워집니다. 이는 특히 큰 코드베이스나 여러 개발자가 협업하는 환경에서 문제가 될 수 있습니다

 

<리액트에서 불변성이 가지는 의의>

리액트에서는 화면을 리렌더링 할지 말지 결정할 때 state의 변화를 확인

===> state가 변했으면 리렌더링 하는 것이고, state가 변하지 않았으면 리렌더링을 하지 않죠.

 

그때, state가 변했는지 변하지 않았는지 확인하는 방법:

state의 변화 전, 후의 메모리 주소를 비교하는 것!!!

 

그래서 만약 리액트에서 원시데이터가 아닌 데이터(참조형)를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 가하면 값은 바뀌지만 메모리주소는 변함이 없게 되는것임으로, 리렌더링이 일어나지 않음.

 

==> 참조형 데이터의 수정 또는 복사를 할때, 원래 요소의 불변성을 지키며, 메모리 주소를 변경할 수 있는 방식으로 수정, 복사를 해야함!!!

 

import React, { useState } from "react";

function App() {
  const [dogs, setDogs] = useState(["말티즈"]);

  function onClickHandler() {
		// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다. 
	  // 그리고 나서 항목을 추가합니다.
    setDogs([...dogs, "시고르자브르종"]);
  }

  console.log(dogs);
  return (
    <div>
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

-- 그래서 배열 같은 거에 요소 추가할 때도, push 보다는 ... 스프레드 연산자 사용해야함.