how to make Component?
1. structure
// components/ExpenseItem.js
function ExpenseItem() {
return <h2>expense item!</h2>;
}
export default ExpenseItem;
// app.js
import ExpenseItem from './components/ExpenseItem';
function App() {
return (
<div>
<ExpenseItem />
</div>
);
}
export default App;
- src/ components | 이 폴더 안에서 각 컴포넌트 별로 파일을 만들어서 작업한다.
- 이 때 App 컴포넌트는 일반적인 UI와는 다른 root 컴포넌트이기 때문에, components 폴더 안으로 이동시키지 않는다.
- 컴포넌트의 파일명 | 대문자로 시작하며, Camel 표기법을 따른다. 파일의 확장자로는 js 또는 jsx를 사용할 수 있다.
- App.js에서 알 수 있듯이 리액트의 컴포넌트란, 함수이다.
- 즉 컴포넌트 파일 안에는, 컴포넌트를 반환하는 함수를 작성한다.
- 컴포넌트의 return 값은 브라우저에 렌더링할 목표 상태(target state)를 정의하는 방식으로 작성되고, 이러한 방식을 declarative approach라고 한다.
- 컴포넌트를 App.js에 import 함으로써 브라우저에 렌더링할 수 있다.
- 컴포넌트 함수의 첫글자는 반드시 대문자로 작성해야하는데, 리액트가 이 대문자로 cumtom html element 인지, 아닌지를 판단하기 때문이다.
- 컴포넌트 함수가 반환하는 jsx 코드는 여러 개의 형제 요소를 반환할 수 없다(하나의 root elemet 만을 반환한다).
2. adding styles
// ExpenseItem.js
import './ExpenseItem.css';
function ExpenseItem() {
return (
<div className='expense-item'>
<strong className='date'>March 28th 2021</strong>
<div className='description'>
<h2>Car Insurance</h2>
<p className='price'>$295.67</p>
</div>
</div>
);
}
export default ExpenseItem;
- jsx 코드의 html 요소에 class를 지정할 때에는 className이라는 속성을 사용해야 한다.
- 컴포넌트와 동일한 이름을 가진 css 파일을 만든다.
- css 파일을 해당 컴포넌트 파일에 import 한다.
3. dynamic data | props
what is props?
function ExpenseItem() {
const expenseDate = new Date(2022, 5, 15);
const expenseTitle = 'Car Insurance';
const expenseAmount = 295.67;
return (
<section className='expense-item'>
<strong className='date'>{expenseDate.toDateString()}</strong>
<div className='description'>
<h2>{expenseTitle}</h2>
<p className='price'>${expenseAmount}</p>
</div>
</section>
);
}
// app.js
function App() {
return (
<div>
<ExpenseItem />
<ExpenseItem />
<ExpenseItem />
</div>
);
}
- curly braces in jsx | {culry braces}를 사용하면, 그 안에서 js expression code를 실행할 수 있다.
- re-usability
- app.js에서 <ExpenseItem /> 컴포넌트를 원하는 만큼 재사용할 수 있지만, 이것이 진정한 의미의 재사용이라고 보기는 어렵다.
- 컴포넌트 내부에 데이터가 고정되어 있어서, 모든 컴포넌트가 같은 데이터를 보여주기 때문이다.
- props | properties, 컴포넌트 외부에서 데이터를 전달하면, 전달받은 데이터에 맞게 UI를 렌더링할 수 있다.
- props은 컴포넌트 함수의 input과도 같다.
- 함수가 input에 따라 다른 output을 반환하는 것처럼, 컴포넌트 함수에 data, 즉 props을 전달함으로써 컴포넌트를 재사용한다.
how to use props?
1. 부모 컴포넌트 => 자식 컴포넌트
// app.js
function App() {
const expenses = [
{ id: 'e1', title: 'Toilet Paper',amount: 94.12, date: new Date(2020, 7, 14) },
{ id: 'e2', title: 'New TV', amount: 799.49, date: new Date(2021, 2, 12) },
{ id: 'e3', title: 'Car Insurance', amount: 294.67, date: new Date(2021, 2, 28) },
];
return (
<div>
<ExpenseItem title={expenses[0].title} amount={expenses[0].amount} date={expenses[0].date}/>
<ExpenseItem title={expenses[1].title} amount={expenses[1].amount} date={expenses[1].date}/>
<ExpenseItem title={expenses[2].title} amount={expenses[2].amount} date={expenses[2].date}/>
</div>
);
}
// ExpenseItem.js
function ExpenseItem(props) {
return (
<section className='expense-item'>
<strong className='date'>{props.date.toDateString()}</strong>
<div className='description'>
<h2>{props.title}</h2>
<p className='price'>${props.amount}</p>
</div>
</section>
);
}
- 각 컴포넌트에 데이터를 전달할 때, html의 property를 설정하는 것처럼 사용하기 때문에 props라는 이름이 붙었다.
- 리액트가 컴포넌트에 전달된 properties를 하나의 객체에 담아 컴포넌트 함수의 첫번째 매개변수로 전달한다.
- 각 컴포넌트는 이제 같은 UI를 가지고 있으면서도, 서로 다른 데이터를 보여줄 수 있다.
2. 자식 컴포넌트 => 부모 컴포넌트
// NewExpense.jsx(부모)
const NewExpense = (props) => {
const savaDataHandler = (data) => {
return {...data, id: Math.random().toString + Math.random().toString}
};
return (
<section className='container new-expense'>
<h1 className='sr-only'>make new expense log</h1>
<NewExpenseForm onSaveData={saveDataHandler} />
</section>
);
};
// NewExpenseForm.jsx(자식)
const NewExpenseForm = (props) => {
// ...
const submitHandler = (e) => {
e.preventDefault();
const newExpenseData = {
title: titleInput,
amount: amountInput,
date: new Date(dateInput),
};
props.saveDataHandler(newExpenseData);
setTitleInput('');
setDateInput('');
setAmountInput('');
};
// ...
}
- 부모 컴포넌트에서 자식 컴포넌트의 props에 데이터를 저장하는 함수를 전달한다.
- 자식 컴포넌트에서 해당 함수에 데이터를 인자로 전달해서 실행한다.
what is Composition? | props.children
composition(합성)이란, 컴포넌트 안에 다른 컴포넌트가 포함되어 있는 것을 말한다. 어플리케이션의 규모가 커짐에 따라 UI가 복잡해지기 때문에 composition이 일어나는 것은 당연하다. wrapper component(container)도 이런 composition의 하나로 볼 수 있다.
+) modal이나 경고창은 대표적인 wrapper component이다. 이런 컴포넌트의 스타일링을 미리 지정해 놓으면 css 코드의 중복을 피할 수 있다.
// Card.js
import './Card.css'; // Card에 공통적으로 적용할 스타일을 작성한다
function Card(props) {
// 여기에서 card는 항상 적용할 default class이다(띄어쓰기).
const classes = 'card ' + props.className;
return (
<div className={classes}>{props.children}</div>
)
}
export default Card;
// Item.js
function Item(props) {
return (
<Card className='card item'>
<h1>{props.title}</h1>
<p className='price'>${props.contents}</p>
</Card>
);
}
export default Item;
- props.children
- 일반적으로 cumstom tag는 자식 요소를 가질 수 없다.
- props.childeren는 해당 컴포넌트의 자식 요소를 가리킨다.
- props.className
- 일반적으로 cumstom tag는 className과 같은 속성을 가질 수 없다.
- 사용하고 싶은 속성이 있다면, 그 속성을 코드로 명시해야 한다.
+) 컴포넌트에서 fontAwesome 이용하기
$ yarn add @fortawesome/fontawesome-free
// index.js
import '@fortawesome/fontawesome-free/js/all.js';
index.js에 fontawesome을 import한 이유는, 아이콘이 사용되는 모든 모듈에서 import를 하는 것보다, index.js 한 곳에 import 해 두는 것이 간단하고 빠르게 아이콘을 출력할 수 있기 때문이다.
'Study > React' 카테고리의 다른 글
Event & useState (0) | 2022.06.16 |
---|---|
JSX (0) | 2022.06.16 |
What is React? (0) | 2022.06.16 |
MVC & Dependency Injection (0) | 2022.04.29 |
PostCSS (0) | 2022.04.23 |