컴포넌트
App() 함수에 모든 코드를 다 작성해도 동작하는 데에는 이상이 없지만 모듈화를 할 수 없고, 가독성이 떨어진다. 그래서 입력을 받아 출력하는 역할을 하는 컴포넌트를 사용한다.
클래스형
리액트에서 클래스 컴포넌트를 만들려면 컴포넌트를 상속해야 한다. extends 키워드로 컴포넌트를 상속할 수 있다.
그리고, 클래스 안에 render 메서드를 작성해야 한다. 해당 메서드에서 리턴하는 값이 화면에 보이게 된다.
ClassCom.tsx
import { Component, ReactNode } from "react";
class ClassCom extends Component {
render(): ReactNode {
return (
<div>
클래스형 컴포넌트
</div>
)
}
}
export default ClassCom;
App.tsx
import React from 'react';
import './App.css';
import ClassCom from './ClassCom';
function App() {
return (
<div className='container'>
<ClassCom></ClassCom>
</div>
);
}
export default App;
함수형
render을 작성하지 않고 바로 return을 작성한다. return에 적힌 것이 화면에 보인다.
FuncCom.tsx
import React from 'react';
const FuncCom = () => {
return (
<div>
함수형 컴포넌트
</div>
)
}
export default FuncCom;
App.tsx
import React from 'react';
import './App.css';
import FuncCom from './FuncCom';
function App() {
return (
<div className='container'>
<FuncCom></FuncCom>
</div>
);
}
export default App;
현재 리액트 공식 문서에서는 함수형 컴포넌트를 사용할 것을 권장하고 있다. 원래는 state와 life cycle 관련 기능을 클래스형 컴포넌트에서만 사용할 수 있었는데, 16.8 버전부터는 함수형 컴포넌트에서도 hook을 사용하여 state와 life cycle 기능을 구현할 수 있게 되었기 때문이다. 또한, 함수형 컴포넌트가 클래스형 컴포넌트보다 메모리 자원을 덜 사용하고, 빌드 후 파일 크기가 더 작다. 그리고 render() 메서드를 작성하지 않기 때문에 컴포넌트 마운트 속도가 더 빠르고, 가독성도 좋다.
데이터 반복 처리
배열에 있는 원소들을 각각 li 태그에 담아서 화면에 출력시켜 보자.
배열의 메서드 중에 map()이라는 메서드가 있다. 배열의 각 원소에 대해 콜백 함수에서 원하는 동작을 한 후, 값을 리턴하면 그 값들을 모은 배열을 반환해 주는 기능을 한다. 아래 코드와 같이 li 태그에 해당 원소를 적어서 리턴해주면 여러 li들이 반환된다.
li에 key를 쓰는 이유는 고유한 식별자가 있어야 데이터를 핸들링할 수 있기 때문이다. 근데 안 쓴다고 오류가 발생하지는 않지만 key를 써주는 것이 좋다.
import React, { useState } from 'react';
const MapTest = () => {
const fruits: string[] = ['apple', 'banana', 'orange'];
return (
<div>
<h2>과일</h2>
<ul>
{fruits.map((fruit, index) => {
return <li key={index}>{fruit}</li>
})}
</ul>
</div>
)
}
export default MapTest;
Todolist
먼저 Todolist의 각 항목의 타입을 정의한다. text는 할 일, isChecked는 완료 여부를 true/false로 나타낸다. id는 각 li에 부여되고, 체크박스가 클릭되면 id를 이용해 텍스트에 밑줄을 긋게 해 준다.
type Todo = {
id: number;
text: string;
isChecked: boolean;
};
TodoList라는 함수형 컴포넌트를 선언한다. 이 컴포넌트의 타입은 FC(Function Component)로, props의 타입을 명시해 주기 위해 사용된다.
체크박스가 클릭되면 isChecked를 true로 바꾸고, 텍스트에 밑줄을 그어야 한다. 즉, 데이터에 변경이 일어나면 랜더링을 다시 해야 한다. 바인딩된 데이터가 변경되면 자동으로 재렌더링될 수 있도록 state를 사용한다. useState에 초기 값을 넣어서 초기화할 수 있다. useState는 배열을 반환하는데, 첫 번째 요소는 현재 상태이고, 우 번째 요소는 Setter 함수이다.
const TodoList: React.FC = () => {
...
const [todos, setTodos] = useState<Todo[]>([
{id: 1, text: '공부하기', isChecked: false},
{id: 2, text: '잠자기', isChecked: false},
{id: 3, text: '미팅하기', isChecked: false}
]);
...
}
체크 박스에 클릭이 발생하면 해당 이벤트를 핸들링하는 함수 handleCheckedChange를 호출한다.
해당 함수에서 Setter을 이용해 해당 id를 가진 객체의 isChecked 여부를 반전시킨다. map을 사용하였기 때문에 새로운 배열이 반환되므로 state가 변경된다. 따라서 재렌더링이 발생한다.
isChecked를 이용해 밑줄이 있는 텍스트를 출력할 것인지, 텍스트를 그대로 출력할 것인지를 삼항 연산자로 결정한다.
const TodoList: React.FC = () => {
...
const handleCheckedChange = (itemId: number) => {
setTodos(prevItems =>
prevItems.map(item =>
item.id === itemId ? {...item, isChecked: !item.isChecked} : item
)
)
}
return (
<div>
<h1>{title}</h1>
<div className='container'>
<div className='board'>
<ul>
{
todos.map((todo, idx) =>
<li key={todo.id}>
<input type="checkbox"
onChange={() => handleCheckedChange(todo.id)}/>
{
todo.isChecked ?
<del>{todo.text}</del> :
<span>{todo.text}</span>
}
</li>
)
}
</ul>
</div>
</div>
</div>
)
}
배운 점
- 컴포넌트는 입력(props)을 받아 출력(Element)하는 역할을 한다.
- 컴포넌트를 사용하면 코드를 재사용할 수 있고, 가독성이 좋아지며, 유지보수 비용이 줄어든다.
- 현재 리액트 공식 문서에서는 함수형 컴포넌트를 사용할 것을 권장하고 있다. 함수형 컴포넌트가 클래스형 컴포넌트보다 메모리 자원을 덜 사용하고, 빌드 후 파일 크기가 더 작다. 그리고 render() 메서드를 작성하지 않기 때문에 컴포넌트 마운트 속도가 더 빠르고, 가독성도 좋다.
- 배열의 map 메서드를 사용하면 데이터를 반복적으로 처리할 수 있다.
- useState를 사용하여 데이터에 변경이 발생하면 자동으로 재렌더링할 수 있다.
'데브코스' 카테고리의 다른 글
[12주차 - DAY2] 게시판 만들기(1) (0) | 2024.05.14 |
---|---|
[12주차 - DAY1] TodoList 만들기(2) (0) | 2024.05.13 |
[11주차 - DAY4] 리액트(1) (0) | 2024.05.09 |
[11주차 주간 발표] const assertion과 RORO 패턴 (0) | 2024.05.09 |
[11주차 - DAY3] 리터럴, 클래스 (0) | 2024.05.08 |