
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
리렌더링 되는 조건
- 자신이 관리하는 State 의 값이 변경 되었을 때
- 자신이 제공받는 props의 값이 변경 되었을 때
- 부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 리렌더링 됨
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 |