프론트엔드(Web)/React

React 작동 원리 및 특징, JSX 문법

만능 엔터테이너 2024. 9. 29. 15:01
728x90
반응형
SMALL

 

[리액트의 특징]

  1. 컴포넌트를 기반으로 UI를 표현한다.
  2. 화면 업데이트 구현이 쉽다.
  3. 화면 업데이트가 빠르게 처리된다.

[컴포넌트를 기반으로 UI를 표현한다.]

컴포넌트 - 화면을 구성하는 요소, UI를 구성하는 요소

 

각각의 JAVASCRIPT 파일로 모듈화하여 레고 블록처럼 만들 수 있음 ⇒ 중복 코드를 재사용 가능!

페이지의 모든 요소를 컴포넌트화하여 생성 → 이후 불러와서 사용


[화면 업데이트 구현이 용이]

업데이트란? 사용자의 행동(클릭, 드래그)에 따라 웹 페이지가 스스로 모습을 바꿔 상호작용 하는 것

렌더링? UI 요소를 화면에 그려내는 것을 말함

 

선언형 프로그래밍 : 과정은 생략하고 목적만 간결히 명시하는 방법 ex) 식당에 가서 주문하는 것처럼 “토마토 파스타 하나 주세요” 처럼 말하고 토마토 파스타 만드는 방법은 알 필요가 없음

명령형 프로그래밍 : 목적을 이루기 위한 모든 일련의 과정을 설명하는 방식

ex) 마치 진상 손님처럼 주문하고 토마토 파스타 만드는 방법을 일일히 명시하여야 하기 때문


[화면 업데이트가 빠르게 처리된다]

가상의 DOM의 각각의 업데이트를 처리하고 1번에 실제 DOM에 반영을 하기에 렌더링이 빠름!


React App 생성하기

기본설정이 이미 완료된 React App 생성 툴

React 공식 문서에서도 권장되고 있음

 

[Vite 설치 순서]

  1. npm create vite@latest
  2. Project name 입력
  3. 진행할 프레임워크 설치 → React
  4. 사용할 언어 선택
  5. 이후 생성한 project 폴더 명이 Root 폴더가 되도록 설정
  6. 이후 npm i로 터미널 창에 입력하고 필요한 모듈을 설치
  • public 폴더 내부
    • svg, png, jpg 와 같은 이미지 파일이거나 font 혹은 동영상 등 코드가 아닌 정적인 파일 저장소
  • src 폴더 내부
    • 실제 react 및 javscript 코드를 작성할 폴더
  • assets 폴더 내부
    • svg, png, jpg 와 같은 이미지 파일 저장소
  • index.html 파일
    • 리액트 앱의 기본 틀을 역할을 하는 html 코드 정보

[npm run dev]

"no-unused-var": "off",
"react/prop-types": "off",

"no-unused-var": "off":
설명: JavaScript에서 사용되지 않은 변수를 경고하는 규칙인 no-unused-var를 끕니다. 즉, 사용하지 않은 변수가 있어도 경고하지 않도록 설정한 것입니다.

"react/prop-types": "off":
설명: React 컴포넌트에서 prop-types로 전달되는 props의 타입을 명시하지 않아도 경고하지 않도록 하는 설정입니다. 보통 prop-types는 props의 타입을 명확하게 하기 위해 사용되지만, 이 규칙을 끄면 타입을 명시하지 않아도 문제가 되지 않습니다.
따라서 이 두 설정은 ESLint가 사용하지 않은 변수를 허용하고, React에서 prop-types 사용에 대한 경고를 무시하도록 만든 것입니다.

---eslint.config.js----

import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'

export default [
  { ignores: ['dist'] },
  {
    files: ['**/*.{js,jsx}'],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: 'latest',
        ecmaFeatures: { jsx: true },
        sourceType: 'module',
      },
    },
    settings: { react: { version: '18.3' } },
    plugins: {
      react,
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
    },
    rules: {
      ...js.configs.recommended.rules,
      ...react.configs.recommended.rules,
      ...react.configs['jsx-runtime'].rules,
      ...reactHooks.configs.recommended.rules,
      'react/jsx-no-target-blank': 'off',
      'react-refresh/only-export-components': [
        'warn',
        { allowConstantExport: true },
      ],
      //아래 코드 복사!
      "no-unused-var": "off",
      "react/prop-types": "off",
    },
  },
]

TailwindCSS 설치 방법 in Vite : https://tailwindcss.com/docs/guides/vite

[React App의 구동 원리]

http://localhost:5173 : 자신의 컴퓨터를 의미(포트번호 :5173)

→ localhost는 자신의 컴퓨터에만 작동하게 되므로 다른 사람의 컴퓨터에는 작성되지 않습니다!

createRoot(document.getElementById('root')).render(
index.html에 나와 있는 내용을 불러오는 역할
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'  ->  Root 파일
import './index.css' -> css 파일 속성  

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)
-- App.jsx --
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

-- index.html --

[React 컴포넌트] - 반드시 대문자로 생성

App.jsx 컴포넌트 내부에 components를 불러 오게 되면

App 컴포넌트가 부모 컴포넌트 (즉, Root 컴포넌트 - 관례상 App 사용!)

App 컴포넌트에 import하는 컴포넌트는 자식 컴포넌트 (함수 컴포넌트의 return 값을 설정)

 

만약, 이렇게 main.jsx 에서 <App />을 <Hello />로 변경하게 되면 루트 컴포넌트는 Hello가 되는 것!

📍여기서 한 파일명.jsx 에 전부 작성하기 보다는 컴포넌트별로 파일을 만들어 import 하여 불러오는 형식으로 작성하는 것을 권장합니다.

 

컴포넌트를 만들 때, src 하위 "component" 파일 내부에 파일명.jsx를 작성!


[JSX란?] - 리액트에서 사용하는 확장된 자바스크립트 문법

[주의 사항]

숫자 및 문자열 값이면 { } 객체 데이터 내부에 만들어서 렌더링 하도록 만들 수 있음

  1. 중괄호 { } 내부에는 자바스크립트 표현식만 넣을 수 있다. 

 → 한 줄로 표현이 가능한 식들 가능 / 😎if문 혹은 for문은 1줄로 표현이 되지 않기에 중괄호 내부에 들어갈 수 없음

 

   2. 숫자, 문자열, 배열 값만 렌더링 된다.

ex) bolean , undefined, null 값은 화면에 렌더링 되지 않습니다. ex) true, undefined,null,obj... 등

😎여기서 객체 값은 렌더링 되지 않아 {obj.a} 등 객체 데이터 내부에 문자 및 숫자값으로 변경하여야 합니다.

 

3. 모든 태그는 닫혀 있어야 한다.

4. 최상위 태그는 반드시 하나여야만 한다.

이렇듯, 최상위 태그가 <main> ~ </main> 태그와 <div> ~ </div> 태그 이렇게 2가지가 존재할 경우 오류가 발생하게 됩니다.

따라서, 최상위 태그는 <main> ~ </main>로 작성하여야 합니다.

만약, 최상위 태그가 필요없을 경우 <> ~ </> 처럼 빈태그로 반환하면 됩니다.

const App = () => {
  const user = {
    name: "전용학",
    isLogin: true,
  };
  if (user.isLogin) {
    return <div>로그아웃</div>;
  } else {
    <div>로그아웃</div>;
  }
  // return <>{user.isLogin ? <div>로그인</div> : <div>로그아웃</div>}</>;
};
export default App;

객체 내부에 로그인을 구현하도록 설정!

[JSX에서 style을 설정하는 방법]

style={{
backgroundColor: “red”,
borderBottom: "5px solid blue",
}}
  • - css 형식이 아닌 카멜 케이스로 작성!

  • 별도의 css 파일을 만들어 css 작성하기
.logout {
background-color: red;
border-bottom: 5px solid green;
}

Main.css(임의의 파일)을 작성하고 App.jsx에서 import “./Main.css”를 입력하고 나서

태그 내부 속성에 className=”logout” 이라고 작성하면 됩니다.

[Props - 컴포넌트에 값 전달하기]

이렇게 하나의 Button 컴포넌트를 만들어서 image와 text만 바꾸어서 반복 렌더링하여 구성하는 기능

😎props란 부모 컴포넌트로부터 자식 컴포넌트에 데이터를 보낼 수 있게 해주는 방법이다.

const Button = (props) => {
  console.log(props);
  return (
    <>
      <button style={{ color: props.color }}>
        {props.text} - {props.color.toUpperCase()}
      </button>
    </>
  );
};

Button.defaultProps = {
  color: "black",
};
export default Button;
--Button.jsx---
여기서 Button 컴포넌트에 text와 color 
속성을 지정하고 나서 1번째 버튼요소를 제외한 2,3번째는 
color 속성이 없기 때문에 이럴때는, Button.defaultProps = 
{ } 내부에 color의 기본값을 지정하여 오류가 발생하지 않
도록 방지함!
import Header from "./components/Header";
import Main from "./components/Main";
import Footer from "./components/Footer";
import Button from "./components/Button";

const App = () => {
  return (
    <>
      <Header />
      <Main />
      <Footer />
      <Button text={"메일"} color={"red"} />
      <Button text={"카페"} />
      <Button text={"블로그"} />
    </>
  );
};
export default App;
---App.jsx---

props - 객체 형태 { “” }로 전달 받는다는 것을 앎 - 비구조화 할당 방법을 통한 Props 전달

import Header from "./components/Header";
import Main from "./components/Main";
import Footer from "./components/Footer";
import Button from "./components/Button";

const App = () => {
  const buttonProps = {
    a: 1,
    b: 2,
    c: 3,
  };
  return (
    <>
      <Header />
      <Main />
      <Footer />
      <Button {...buttonProps} text={"메일"} color={"red"} />
      <Button text={"카페"} />
      <Button text={"블로그"} />
    </>
  );
};
export default App;
다음과 같이 값을 전달할 내용이 여러 개일 경우 배열로 만들
어 a:1, b:2, c:3 이렇게 작성하고 ... 전개연산자로 객체 형식
으로 불러오면 됩니다.

😎 props에서 값은 부모 컴포넌트에서 자식 컴포넌트로만 전달 가능! / 반대는 React에서 불가능함


[이벤트 핸들링]

onClick = {onClickHandler} 이렇게 속성을 작성합니다.

/* eslint-disable react/prop-types */
const Button = ({ text, color, children }) => {
  const onClickHandler = () => {
    console.log({ text });
  };
  return (
    <>
      <button onClick={onClickHandler} onMouseEnter={onClickHandler}>
        {text} - {color.toUpperCase()}
        {children}
      </button>
    </>
  );
};

Button.defaultProps = {
  color: "black",
};
export default Button;**

[이벤트 객체] (e) ⇒ { }  / 공식 문서: https://react.dev/learn/responding-to-events

합성 이벤트 - 모든 웹 브라우저의 이벤트 객체를 하나로 통일한 형태

이벤트 사용 시 주의사항

1. 이벤트 이름은 😎카멜표기법으로 작성

     HTML에서는 onclick으로 작성하지만 리액트에서는 카멜 표기법으로 onClick으로 작성해야한다
2. 이벤트에는 😎함수 형태의 값을 전달

    HTML에서 이벤트를 설정할 때 ""안에 실행할 코드를 넣었지만, 리액트에서는 함수 형태의 객체를 전달한다. 애로우(화     살표)함수 문법을 사용하거나 혹은 외부에 미리 함수를 만들어서 전달하기도한다.

3. 😎DOM요소에만 이벤트를 설정할 수 있다.

    div, button, input, form, span 등의 DOM요소에는 이벤트를 설정할 수 있지만, 직접만든 리액트 컴포넌트에는 이벤트를  자체적으로 설정할 수 없다.

이러한 Cross Browsing Issue를 해결하는 방법이 합성 이벤트


[State - 상태 관리하기]

이렇듯, 하나의 컴포넌트 안에 여러 개의 state를 작성할 수 있습니다.

  const [state, setState] = useState();
const [현재 값, 현재 값을 변경시키는 함수] = useState(초기값);

또한, const [ state, setState] = useState(””)에서 userState값에 “” 혹은 0 등 기본값을 작성하지 않으면 화면에 렌더링이 되지 않을 수 있습니다. 따라서, 상태 초기값을 명시적으로 설정하는 것을 권장합니다.


리액트가 Re-rendering 되는 3가지 상황

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

const Bulb = () => {
  const [light, setLight] = useState("");
  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>
    </>
  );
};

const Counter = () => {
  const [count, setCount] = useState(0); // 초기값 설정
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
};

const App = () => {
  return (
    <>
      <Bulb />
      <Counter />
    </>
  );
};
export default App;

이렇게 컴포넌트를 나누게 되면 화면에는 리렌더링 되는 상황을 방지하여 더 빠르게 상태를 업데이트 할 수 있습니다.

→ 이 컴포넌트를 Bulb.jsx 와 Counter.jsx로 파일로 분리하여 모듈화하여 사용하는 것을 권장합니다.


[State로 사용자 입력 관리하기]

 

📍이벤트 함수를 사용할 때 반드시 사용해야 할 것

(e) ⇒ set함수명(e.target.value)

onChange={onChangeCountry} 와 value={country} 반드시 2개의 필수 요소를 작성해야 합니다.

 

onChange 이벤트 함수 : onchange 이벤트는 HTML 요소의 값이 변경될 때 발생하는 이벤트입니다. 이 이벤트는 주로 <input>, <select>, <textarea>와 같은 폼 요소에서 많이 사용됩니다. 값이 변경되면 이벤트 핸들러를 호출하여 특정 작업을 수행할 수 있습니다.

e.preventDefault()

기본(default)를 막다(prevent)라는 단어에서 유추할 수 있듯이 html에서 표준으로 제공하는 태그의 기본 이벤트 발생을 막는 메서드입니다. 

  e.preventDefault(); // 폼 제출 시 페이지 리로드를 방지

사용 예시:

  • 링크 클릭 시 페이지 이동을 막고, 대신 자바스크립트로 다른 동작을 수행하고 싶을 때.
  • 폼 제출 시 페이지가 새로고침되지 않도록 하고, AJAX로 데이터를 처리하고 싶을 때.

e.stopPropagation()

전파(Propagation)를 중지한다(stop)라는 의미처럼 상위 엘리먼트로의 이벤트 전파(이벤트 버블링)을 막기 위한 메서드입니다.

 

사용 예시:

  • 중첩된 요소에서 하위 요소의 이벤트가 상위 요소에 전달되지 않도록 하고 싶을 때.
  • 버튼 클릭 시 하위 요소에서만 특정 동작을 하고, 상위 요소는 무시하고 싶을 때
// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개
import { useState } from "react";

const Register = () => {
  const [name, setName] = useState("이름");

  const onChangeName = (e) => {
    setName(e.target.value);
    // onChangeName의 인자로 이벤트를 전달하면 전달한 이벤트
    의 값으로 상태를 변경토록 해주는 로직
  };
  return (
    <div>
      <input onChange={onChangeName} type="text" placeholder="이름" />
      {name} // props로 사용자의 입력값에 따라 변동
    </div>
  );
};
export default Register;

이벤트 부분에서 (e) ⇒ setName(e.target.value)에서 작성하게 되면 div 태그 내부에서 input

리액트의 useState를 이용하여 사용자의 입력 값을 저장하고 처리할 때에는 onChange 속성과 더불어 value={ } 속성까지 사용하여야 합니다.

// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개
import { useState } from "react";

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

  const onChangeBirth = (e) => {
    setBirth(e.target.value);
  };

  const onChangeName = (e) => {
    setName(e.target.value);
  };
  return (
    <div>
      <div>
        <input
          value={name}
          onChange={onChangeName}
          type="text"
          placeholder="이름"
        />
      </div>
      <div>
        <input value={birth} onChange={onChangeBirth} type="date" />
      </div>
    </div>
  );
};
export default Register;

이렇게 작성하게 되면 날짜를 선택하게 되면 상태 값이 저장 되는 것을 볼 수 있습니다. {birth} 값을 추가하여 나타나게 되면

이렇게 나타나게 할 수 있습니다.

// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개
import { useState } from "react";

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

  const onChangeCountry = (e) => {
    setCountry(e.target.value);
  };

  const onChangeBirth = (e) => {
    setBirth(e.target.value);
  };

  const onChangeName = (e) => {
    setName(e.target.value);
  };
  return (
    <div>
      <div>
        <input
          value={name}
          onChange={onChangeName}
          type="text"
          placeholder="이름"
        />
      </div>
      <div>
        <input value={birth} onChange={onChangeBirth} type="date" />
      </div>
      {birth}
      <div onChange={onChangeCountry} value={country}>
        <select>
          {/* select 태그에 빈 태그를 추가하려면 바로 아래처럼 아무것도 입력하지 않으면 됩니다. */}
          <option value=""></option>
          {/* 이렇게 설정하게 되면 만약 한국을 선택할 때, 상태 값은 kr로 나오고 한국으로 나오지 않습니다. */}
          <option value="kr">한국</option>
          <option value="us">미국</option>
          <option value="uk">영국</option>
          <option value="gr">독일</option>
          <option value="fr">프랑스</option>
          <option value="ita">이탈리아</option>
        </select>
        {country}
      </div>
    </div>
  );
};
export default Register;

여기서 option 태그에 value 값으로 kr, us 등 간격하게 지정하게 해 놓고 사용자 화면에는 한국이면 상태 값은 kr로 업데이트 되도록 하는 형식이 좋습니다.

// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개
import { useState } from "react";

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

  const onChangeBio = (e) => {
    setBio(e.target.value);
  };

  const onChangeCountry = (e) => {
    setCountry(e.target.value);
  };

  const onChangeBirth = (e) => {
    setBirth(e.target.value);
  };

  const onChangeName = (e) => {
    setName(e.target.value);
  };
  return (
    <div>
      <div>
        <input
          value={name}
          onChange={onChangeName}
          type="text"
          placeholder="이름"
        />
      </div>
      <div>
        <input value={birth} onChange={onChangeBirth} type="date" />
      </div>
      {birth}
      <div onChange={onChangeCountry} value={country}>
        <select>
          {/* select 태그에 빈 태그를 추가하려면 바로 아래처럼 아무것도 입력하지 않으면 됩니다. */}
          <option value=""></option>
          {/* 이렇게 설정하게 되면 만약 한국을 선택할 때, 상태 값은 kr로 나오고 한국으로 나오지 않습니다. */}
          <option value="kr">한국</option>
          <option value="us">미국</option>
          <option value="uk">영국</option>
          <option value="gr">독일</option>
          <option value="fr">프랑스</option>
          <option value="ita">이탈리아</option>
        </select>
        {country}
      </div>
      <div>
        <textarea onChange={onChangeBio} value={bio} placeholder="자기소개">
          {bio}
        </textarea>
        {bio}
      </div>
    </div>
  );
};
export default Register;

위에 작성한 내용이 동작하는 방식이 동일하여 1개의 state로 작성하도록 만들려면 아래와 같이 작성하여야 합니다.

**// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개
import { useState } from "react";

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

  const onChangeBio = (e) => {
    setInput({
      ...input,
      bio: e.target.value,
    });
  };

  const onChangeCountry = (e) => {
    setInput({
      ...input,
      country: e.target.value,
    });
  };

  const onChangeBirth = (e) => {
    setInput({
      ...input,
      birth: e.target.value,
    });
  };

  const onChangeName = (e) => {
    setInput({
      ...input, // name값을 제외한 나머지 값들을 변경하지 않을 상태로 나타냄
      name: e.target.value, // 변경하고자하는 props의 값만 바꾸어야 됨
    });
  };
  return (
    <div>
      <div>
        <input
          value={input.name}
          onChange={onChangeName}
          type="text"
          placeholder="이름"
        />
      </div>
      <div>
        <input value={input.birth} onChange={onChangeBirth} type="date" />
      </div>
      {input.birth}
      <div onChange={onChangeCountry} value={input.country}>
        <select>
          {/* select 태그에 빈 태그를 추가하려면 바로 아래처럼 아무것도 입력하지 않으면 됩니다. */}
          <option value=""></option>
          {/* 이렇게 설정하게 되면 만약 한국을 선택할 때, 상태 값은 kr로 나오고 한국으로 나오지 않습니다. */}
          <option value="kr">한국</option>
          <option value="us">미국</option>
          <option value="uk">영국</option>
          <option value="gr">독일</option>
          <option value="fr">프랑스</option>
          <option value="ita">이탈리아</option>
        </select>
        {input.country}
      </div>
      <div>
        <textarea
          onChange={onChangeBio}
          value={input.bio}
          placeholder="자기소개"
        >
          {input.bio}
        </textarea>
        {input.bio}
      </div>
    </div>
  );
};
export default Register;
// 리팩토링 결과, ...input을 선언하여야지 다른 값도 값이
없다고 상태가 업데이트 됨**

아래와 같이 통합 이벤트 핸들러로 설정하여 적용할 수 있음

// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개
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
          value={input.name}
          onChange={onChange}
          type="text"
          placeholder="이름"
        />
      </div>
      <div>
        <input value={input.birth} onChange={onChange} type="date" />
      </div>
      {input.birth}
      <div onChange={onChange} value={input.country}>
        <select>
          {/* select 태그에 빈 태그를 추가하려면 바로 아래처럼 아무것도 입력하지 않으면 됩니다. */}
          <option value=""></option>
          {/* 이렇게 설정하게 되면 만약 한국을 선택할 때, 상태 값은 kr로 나오고 한국으로 나오지 않습니다. */}
          <option value="kr">한국</option>
          <option value="us">미국</option>
          <option value="uk">영국</option>
          <option value="gr">독일</option>
          <option value="fr">프랑스</option>
          <option value="ita">이탈리아</option>
        </select>
        {input.country}
      </div>
      <div>
        <textarea
          onChange={onChange}
          value={input.bio}
          placeholder="자기소개"
        >
          {input.bio}
        </textarea>
        {input.bio}
      </div>
    </div>
  );
};
export default Register;


[useRef - 컴포넌트의 변수 생성하기]

import { useState, useRef } from "react";

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

  const countRef = useRef(0);
  const inputRef = useRef(null);

  const onSubmit = () => {
    if (input.name === "") {
      // 이름을 입력하는 DOM 요소에 포커스를 맞춤
      inputRef.current.focus();
    }
  };

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

  return (
    <div>
      <div>
        <input
          ref={inputRef}
          value={input.name}
          onChange={onChange}
          name="name" // 이름을 입력하는 인풋의 name 속성 추가
          type="text"
          placeholder="이름"
        />
      </div>
      <div>
        <input
          value={input.birth}
          onChange={onChange}
          name="birth" // 생년월일 인풋의 name 속성 추가
          type="date"
        />
      </div>
      <div>
        <select
          onChange={onChange}
          value={input.country}
          name="country" // select의 name 속성 추가
        >
          <option value=""></option>
          <option value="kr">한국</option>
          <option value="us">미국</option>
          <option value="uk">영국</option>
          <option value="gr">독일</option>
          <option value="fr">프랑스</option>
          <option value="ita">이탈리아</option>
        </select>
      </div>
      <div>
        <textarea
          onChange={onChange}
          value={input.bio}
          name="bio" // textarea의 name 속성 추가
          placeholder="자기소개"
        ></textarea>
      </div>
      <button onClick={onSubmit}>제출</button>
    </div>
  );
};

export default Register;


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

class 컴포넌트 - 문법이 복잡함(사용 x)

[3가지 hook 관련된 팁]

  1. 함수 컴포넌트, 커스텀 훅 내부에서만 호출 가능

이렇게 컴포넌트 바깥에서 호출하게 되면 즉시 오류가 발생합니다.

   2.조건부로 호출될 수는 없다. (조건문과 반복문 내부에서 훅을 호출할 수 는 없는 것을 의미)

   3.커스텀 훅을 직접 만들 수 있다.

여기서 드래그한 부분들을 커스텀 훅을 작성하려고 하려면 드래그 한 내용을 복사하여 아래와 같이 getInput 을 만들어 사용하면 됩니다.

 

이때, 커스텀 훅을 만드는 방법은 함수의 이름 앞에 use를 붙여 만약에, getInput을 커스텀 훅으로 만들면 useInput 이렇게 작성하여 만들면 됩니다.


  • 컴포넌트 간 데이터를 이동 ⇒ props 이용
  • But props는 오직 부모에서 자식으로만 전달 가능!

  • State Lifting (상태 끌어올리기)
  • 데이터는 부모에서 자식으로 (위 → 아래) 흐르게 됨
**import "./App.css";
import Viewer from "./components/Viewer";
import Controller from "./components/Controller";
import { useState, useEffect } from "react";
function App() {
  const [count, setCount] = useState(0);
  const 
  useEffect(() => {
    console.log(`count: ${count}`);
  }, [count]);

  const onClickButton = (value) => {
    setCount(count + value);
  };
// value를 선언한 이유는 props로 전달해서 매개변수에 
다른 숫자를 대입하여 숫자의 증감을 제어하기 위해서
  const onResetButton = () => {
    setCount(0);
  };
  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <Viewer count={count} />
      </section>
      <section>
        <Controller
          onClickButton={onClickButton}
          onResetButton={onResetButton}
        />
      </section>
    </div>
  );
}

export default App;**

**const Controller = ({ onClickButton, onResetButton }) => {
  return (
    <>
      <button
        onClick={() => {
          onClickButton(-1);
        }}
      >
        -1
      </button>
      <button
        onClick={() => {
          onClickButton(-10);
        }}
      >
        -10
      </button>
      <button
        onClick={() => {
          onClickButton(-100);
        }}
      >
        -100
      </button>
      <button
        onClick={() => {
          onClickButton(+100);
        }}
      >
        +100
      </button>
      <button
        onClick={() => {
          onClickButton(+10);
        }}
      >
        +10
      </button>
      <button
        onClick={() => {
          onClickButton(+1);
        }}
      >
        +1
      </button>
      <button onClick={onResetButton}>Reset</button>
    </>
  );
};
export default Controller;
 --- controller.jsx ---**
 

TodoList 프로젝트

gap: 10px은 display가 flex인 경우에만 설정이 가능함!
728x90
반응형
LIST