
You will learn
- How to create and nest components
- How to add markup and styles
- How to display data
- How to render conditions and lists
- How to respond to events and update the screen
- How to share data between components
리액트 앱은 컴포넌트들로 만들어져있다. 컴포넌트은 UI은 조각이고 컴포넌트만의 로직을 가지고 있다. 컴포넌트는 버튼보다 작을 수 있고 전체 페이지만큼 클 수도 있다.
function MyButton() {
return (
<button>I'm a button</button>
);
}
// 컴포넌트는 Javascript함수이고 마크업을 리턴한다.
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
// MyButton을 정의하고, 다른 컴포넌트에 중첩시킬 수 있다.
MyButton처럼 컴포넌트는 반드시 대문자로 시작해야한다. (HTML 태그는 소문자로 시작하는 것과 반대)
function MyButton() {
return <button>I'm a button</button>;
}
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
export default는 파일에서 메인 컴포넌트를 지정한다
마크업 문법으로 JSX를 사용하고 이건 선택이다. 하지만 대부분 리액트 프로젝트는 편의를 위해 JSX를 사용한다. JSX는 HTML보다 엄격하다. <br />처럼 반드시 닫혀있어야하고, 여러개의 JSX를 한번에 반환할 수 없다 <div>…</div> 나 <>…</>로 감싸줘야한다
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
리액트에서 CSS class는 className으로 표기한다. HTML에서 class와 같은 역할이다
<img className="avatar" />
그리고 CSS 파일에 CSS를 작성해주면 된다
/* In your CSS */
.avatar {
border-radius: 50%;
}
리액트는 CSS 파일을 추가하는 방법을 규정하지 않습니다.
JSX는 Javascript에 마크업을 넣는다. 중괄호를 사용하면 코드에서 일부 변수를 포함하고 사용자에게 표시할 수 있도록 JavaScript로 "탈출"할 수 있다. 예를 들어 user.name을 표시하고 싶으면 다음과 같이
return (
<h1>
{user.name}
</h1>
);
JSX속성에서 JavaScript로 “탈출”하고 싶으면 “” 대신 중괄호를 사용하면 된다.예를 들어 className=”avatar”는 CSS class 문자열 “avatar”를 전달하지만 src={user.imageUrl}은 JavaScript user.imageUrl의 변수값을 src 속성값으로 사용한 것이다
return (
<img
className="avatar"
src={user.imageUrl}
/>
);
const user = {
name: "Hedy Lamarr",
imageUrl: "https://i.imgur.com/yXOvdOSs.jpg",
imageSize: 90,
};
export default function Profile() {
return (
<>
<h1>{user.name}</h1>
<img
className="avatar"
src={user.imageUrl}
alt={"Photo of " + user.name}
style={{
width: user.imageSize,
height: user.imageSize,
}}
/>
</>
);
}
위 예시에서 style={{}}은 특별한 문법이 아니라, 일반적인 객체{}를 style={}에 넣은 것 뿐이다
리액트에서 조건문을 나타내기 위한 특별한 문법은 없다. 보통 JavaScript에서 작성할 때처럼 똑같은 테크닉을 사용하면 된다. 예를 들어 컴포넌트를 조건문에 적용하게 위해 if를 사용하면 된다.
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);
보다 간결한 코드를 원하면 삼항연산자를 사용하면 된다.
<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>
&& 연산자를 사용하므로 불필요한 else를 사용하지 않을 수도 있다
<div>
{isLoggedIn && <AdminPanel />}
</div>
이러한 모든 접근 방식은 특성을 조건부로 지정하는 데에도 작동
리스트를 렌더링하기 위해 for… 이나 map()을 사용한다
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
컴포넌트에서 map()을 사용하여 배열항목들을 <li>로 반환한다
const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
<li>는 key 속성을 가지고 있다. 목록의 각 항목에 대해 형제 중에서 해당 항목을 고유하게 식별하는 문자열이나 숫자를 전달해야 한다. 일반적으로 키는 데이터베이스 ID와 같은 데이터에서 가져와야 한다. 리액트는 key를 사용하여 나중에 항목을 삽입, 삭제 또는 재정렬하면 어떤 일이 발생했는지 파악한다.
const products = [
{ title: 'Cabbage', isFruit: false, id: 1 },
{ title: 'Garlic', isFruit: false, id: 2 },
{ title: 'Apple', isFruit: true, id: 3 },
];
export default function ShoppingList() {
const listItems = products.map(product =>
<li
key={product.id}
style={{
color: product.isFruit ? 'magenta' : 'darkgreen'
}}
>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
}
event handler 함수를 정의하여 이벤트에 응답할 수 있다
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
onClick={handleClick} 끝에 괄호가 없다. event handler함수를 호출하지말고 전달만 하면된다. 사용자가 버튼을 눌렀을 때 해당 함수를 호출할 것이다.
종종, 컴포넌트가 어떤 정보를 기억하고 출력하고 싶어할 것이다. 예를 들어 버튼을 누를 때마다 숫자가 카운트 되는 것처럼. 이때 state를 컴포넌트에 추가하면 된다. useState를 import한다.
import { useState } from 'react';
state를 정의한다
function MyButton() {
const [count, setCount] = useState(0);
// ...
useState를 통해 두가지를 반환 받는다. 현재 state(count)와 그걸 업데이트 시킬 수 있는 함수(setCount) 아무 이름이나 지어줄 수 있고, 관습적으로 [something, setSomething]이라고 지어준다. 처음 버튼이 표시될 때 useState()에 0을 전달해줬기 때문에 count는 0이다. 그리고 state를 바꾸고 싶을 때 setCount()를 호출하고 새로운 값을 지정해주면 된다. 버튼을 누를 때마다 숫자가 오를 것이다.
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
버튼을 누를 대마다 계속 함수를 호출하고 count는 1, 2 … 증가할 것 이다. 같은 컴포넌트를 여러개 렌더링한다면 각각의 state들이 작동할 것이다
import { useState } from "react";
export default function MyApp() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return <button onClick={handleClick}>Clicked {count} times</button>;
}
버튼들은 각자의 count state를 기억하고 다른 버튼에 아무 영향을 주지 않는다
use로 시작하는 함수를 Hooks라고 부른다. useState를 리액트에 빌트인 되어있는 훅이다. API reference에서 다른 빌트인 훅을 찾아볼 수 있고 이런 것들은 조합하여 커스텀 훅을 만들 수 있다. 훅은 다른 함수들보다 제한적이다. 훅은 다른 훅이나 컴포넌트에서만 호출 가능하다. 만약 useState를 조건문, 반복문 등에서 사용하고 싶으면 새로운 컴포넌트를 거기 만들고 사용해야한다.
이전 예시에서 MyButton은 그것만의(독립적인) count를 가지고 있었다. 그리고 그걸 클릭했을 때 해당 count만 바꼈다. 하지만 데이터를 공유하고 같이 업데이트해야하는 상황이 있다. MyButton이 같은 count를 표시하고 같이 바뀌게 하려면 state를 개별 버튼 "위쪽"에서 모든 버튼을 포함하는 가장 가까운 구성 요소로 이동해야 한다. MyApp에서

이제 두 버튼 중 하나를 클릭하면 MyApp의 카운트가 변경되어 MyButton의 두 카운트가 모두 변경된다. 이를 코드로 표현하는 방법은 다음과 같다.
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
// ... we're moving code from here ...
}
MyApp에서 MyButton들로 state와 공유하는 click handler를 보내준다. 전에 <img>에서 했던 것 처럼 JSX와 중괄호를 사용하여 보내준다
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
이 때 보낸 정보를 props라고 부른다. MyApp은 count, handleClick을 가지고 있고 props로 이것들을 전달해줬다. 부모 구성 요소에서 전달한 props을 읽도록 MyButton을 변경한다.
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}
버튼을 클릭하면 onClick이 작동한다. 버튼들의 onClick prop은 MyApp의 handleClick으로 설정되어 있고 그 내부 코드가 실행된다. setCount(count + 1)를 호출하고, count를 증가시킨다. 새로운 count는 prop으로 각 버튼으로 전달되고 새로운 값을 보여준다. 이것을 ‘lifting state up’이라고 한다. state를 위로 이동하여 컴포넌트 간에 공유
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}
좋은글 감사합니다.