Component

 HTML 태그들을 반환하는 함수를 함수 컴포넌트라고 부른다.

클래스로도 만들 수 있으나 직접 작성해야하는 코드의 양이 어마어마해지기때문에 함수로 만드는게 일반적이고 대중적임 

컴포넌트를 생성하는 함수의 이름은 첫글자가 대문자여야함 

 

 

App component를 root component로 설정하여 사용하는게 관례 

(App component 의 자식 컴포넌트로 있어야 화면에 렌더링되어 보여짐)

 

모듈화를 위해 컴포넌트별로 파일을 각각 나눠서 작성

src - components 파일 생성 - 하위에 파일 생성

 

리액트 앱으로 개발할때는 es모듈시스템을 사용하지만 import 할때 확장자까지는 작성하지 않아도 됨 

 


JSX (JavaScript Extensions)

확장된 자바스크립트의 문법

React.js에서는 JSX 문법을 사용함

const Main = () => {
	const number = 10;
    
    return (
    	<main>
        <h1>main</h1>
        // {} 중괄호를 써서 값을 렌더링 해줄 수 있음 
        <h2>{number % 2 === 0 ? "짝수" : "홀수"}</h2> 
        </main>
    );
};

export default Main;

 

JSX 주의사항

1. {} 중괄호 내부에는 자바스크립트 표현식만 넣을 수 있음 (조건문/반복문 안됨)
2. 숫자, 문자열, 배열 값만 렌더링 됨 (객체, true, null, undefined 같은 것들 안됨)
3. 모든 태그는 닫혀있어야 함
4. 최상위 태그는 반드시 하나여야만 함 (만약 최상위 태그로 묶을게 마땅하지 않으면 <> </> 로 사용할 수도 있음)

 

 

css 스타일 적용

main.jsx

import "./Main.css";

const Main = () => {
	const user = {
    name: "KUN",
    isLogin : true,
    }
    
    if(user.isLogin){
    	return (
        //자바스크립트와 JSX를 같이쓰기때문에 className이라고 표기해서 사용함 (기존 HTML에선 class로만 표기함)
		<div style ={{className="logout}}>로그아웃</div>
    	);
    }
    
};

export default Main;

 

main.css

.logout {
background-color: red;
border-bottom: 5px solid green;
}

 

 


Props

부모컴포넌트가 자식컴포넌트들에게 마치 함수의 인수를 전달하듯이 원하는 값을 전달할 수 있음

이때, 컴포넌트에 전달된 값들을 props라고 함.

props를 이용하면 컴포넌트를 함수 호출하듯이 전달하는 값에 따라서 각각 다른 UI를 렌더링 하도록 만들 수 있음 

 

App.jsx

function App() {
	const buttonProps = {
    text: "메일",
    color: "red",
    a: 1,
    b: 2,
    c: 3,
    };
    
    return (
    	<>
        	<Button {...buttonProps} />
            <Button text={"카페"}>
            <Button text={"블로그"}>
            	<div>
                <Header /> //자식 요소 넣을 수 있음 
                </div>
            </Button>
        </>
    );
}

export default App;

 

Button.jsx

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


//이렇게 표기도 가능
const Button = ({text, color, children}) => {
	console.log(props);
	return (
		<button style={{color: color}}>
		{text} - {color.toUpperCase()}
        {children}
		</button>
	):
};

//React 19부터는 defalutProps 사용이 중지됨 
// 19버전이 정식으로 업데이트 된 후부터는 
// const App = ( {data = '기본값} ) => { ... } 으로 사용가능
Button.defalutProps = {
color:  "black",
};

export default Button;

 


 

이벤트 핸들링 (Event Handling)

이벤트 : 웹 내부에서 발생하는 사용자의 모든 행동 (예: 버튼 클릭, 메세지 입력, 스크롤 등등)

핸들링 : 다루다, 처리하다 

 

const Button = ({text, color, children}) => {
	console.log(props);
	return (
		<button 
        onClick{() => { //이벤트 핸들러
        console.log(text);
        }}
        style={{color: color}}>
		{text} - {color.toUpperCase()}
        {children}
		</button>
	):
};

Button.defalutProps = {
color:  "black",
};

export default Button;

 

선언식으로 써도 됨

주의할점 이벤트 함수는 함수명만 전달하면 됨 (onClickButton() 형태로 쓰지 않아야함)

 

합성 이벤트란?

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

 

Cross Browsing Issue 

브라우저 별 스펙이 달라 발생하는 문제

 

-> 아주 편리하게 해결해주는 것이 바로 리액트의 합성 이벤트 

 

 


 

State (상태 관리)

현재 가지고 있는  형태나 모양을 정의하는 값이면서 변화할 수 있는 동적인 값 

리액트 컴포넌트는 모두 state를 가질 수 있음

State는 컴포넌트의 현재상태를 보관하는 변수

state값에 따라 각각 다른 UI를 화면에 렌더링 한다.

 

컴포넌트가 다시 렌더링 되는 현상을 리액트에서는 리 렌더(Re-Render) or 리 렌더링(Re-Rendering)이라고 부름.

 

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

function App() {
	// 구조분해할당해서 값, 수정할수있는 함수 
    const [count, setCount] = useState(0);
    const [light, setLight] = useState("OFF");
    
    return (
   	<>
    	<div>
         	<h1>{light}</h1>
         	<button onClick{() => {
        	setLight(light === "ON" ? "OFF" : "ON");
        	    }}
            	>
            	{light === "ON" ? "끄기" : "켜기"}
            </button>
    	</div>
        <div>
	        <h1>{count}</h1>
    	    <button onClick{() => {
        	setCount(count + 1);
        	    }}
            	>
            	+
            </button>
       </div>
	</>
    );
}

export default App;

 

 


 

State와 Props

리렌더링 되는 조건

  1. 자신이 관리하는 State 의 값이 변경 되었을 때
  2. 자신이 제공받는 props의 값이 변경 되었을 때
  3. 부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 리렌더링 됨
import "./App.css";
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>
         	<Bulb light={light}>
         	<button onClick{() => {
        	setLight(light === "ON" ? "OFF" : "ON");
        	    }}
            	>
            	{light === "ON" ? "끄기" : "켜기"}
            </button>
    	</div>
        <div>
	        <h1>{count}</h1>
    	    <button onClick{() => {
        	setCount(count + 1);
        	    }}
            	>
            	+
            </button>
       </div>
	</>
    );
}

export default App;

 

-> setCount해도 Bulb 컴포넌트도 리렌더링되는것을 확인할 수 있음

관련없는 스테이트인데 하나의 컴포넌트에 있어서 계속 리렌더링되는것이 나중에 복잡해지면 성능 이슈가 있기때문에

보통 관련 없는 두 개의 스테이트는 서로 다른 컴포넌트로 분리해주는게 좋다.

 

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

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>
    );
};

const Counter= () => {
    const [count, setCount] = useState(0);
    
	return (
    	<div>
        {light === "ON"?(
        <h1 style={{backgroundColor: "orange}}>ON</h1>
        ) : (
        <h1 style={{backgroundColor: "gray}}>OFF</h1>
        )}
         <button onClick{() => {
        	setCount(count + 1);
        	    }}
            	>
            	+
            </button>
        </div>
    );
};


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

export default App;

이렇게하면 불필요한 리렌더링이 발생하지 않음

 

 

추가적으로 정리하자면  파일로도 분리해주면 됨

 

 

 

사용자 입력 

  • input
  • input- type:date
  • select
  • textarea

placeholder : 입력하기 전 힌트텍스트 같은 것 

import { useState } from "react";

// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개

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>
        {country}
      </div>

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

export default Register;

 

 

 

여러개의 State를 하나의 객체로 관리할 수 있음
import { useState } from "react";
// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개

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

  const onChange = (e) => {
    console.log(e.target.name + " : " + e.target.value);
    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;
...input 스프레드 연산자를 사용한 이유는 개별적으로 관리되는 state값을 변경할때 다른 state 값에 영향을 주지 않기 위해서임

 


useRef

새로운 Reference 객체를 생성하는 기능

cosnt refObject = useRef();

이렇게 생성한 레퍼런스 객체는 컴포넌트 내부의 변수로써 일반적인 값들을 저장할 수 있음

 

import { useState, useRef } from "react";

// 간단한 회원가입 폼
// 1. 이름
// 2. 생년월일
// 3. 국적
// 4. 자기소개

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

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

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

  return (
    <div>
      <div>
        <input
          ref={inputRef}
          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>

      <button onClick={onSubmit}>제출</button>
    </div>
  );
};

export default Register;

 

 

Hooks

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

 

2017년도 이전의 리액트에서는 Function 컴포넌트에서는 UI만 렌더링만 할 수 있었고,

대부분 class 컴포넌트를 사용했지만 class 컴포넌트는 문법이 복잡했음.

함수 컴포넌트에서도 class 컴포넌트의 기능을 낚아채듯 가져와서 사용할수 있도록 Hooks이 개발됨 

 

특징

  • React Hooks에는 이름 앞에 use라는 접두사가 붙음 (각각의 메서드들은 영어의 단수형으로 hook으로 부름)
  • 커스텀 훅, 함수 컴포넌트 내부에서만 호출될 수 있음
  • 조건문, 반복문 내부에서는 호출 불가
  • use라는 접두사를 사용해서 custom hook을 만들기 가능 

HookExam.jsx

import useInput from "./../hooks/useInput";

const HookExam = () => {
  const [input, onChange] = useInput();
  const [input2, onChange2] = useInput();

  return (
    <div>
      <input value={input} onChange={onChange} />
      <input value={input2} onChange={onChange2} />
    </div>
  );
};

export default HookExam;

 

useInput.jsx

import { useState } from "react";

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

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

  return [input, onChange];
}

export default useInput;

'Frontend > React.js' 카테고리의 다른 글

[React.js] 페이지 라우팅  (0) 2024.06.12
[React.js] Context  (1) 2024.06.12
[React.js] 최적화  (1) 2024.06.12
[React.js] 라이프 사이클  (0) 2024.06.11
[React.js] React.js 개론  (0) 2024.06.11
복사했습니다!