
prop를 받거나 설정하는 다른 방법이 두가지가 있다.
app.js
const expenses = [
{ .. } ,
{ .. }
];
<ExpenseItem
title={expenses[0].title}
amount={expenses[0].amount}
date={expenses[0].date}
></ExpenseItem>
<ExpenseItem
title={expenses[1].title}
amount={expenses[1].amount}
date={expenses[1].date}
></ExpenseItem>
위와 같은 방식으로 다수의 props을 통해 개별적인 값들을 전달했을 것이다.
하지만 그렇게 하지 않고 단순하게 전체 데이터 포인터를 전달할 수 있다.
app.js
const expenses = [
{ .. } ,
{ .. }
];
<ExpenseItem
expenses = {expenses[0]}
></ExpenseItem>
<ExpenseItem
expenses = {expenses[0]}
></ExpenseItem>
여기서는 배열 안에있는 객체자체를 하나의 prop으로서 전달하는것.
다만 이런식으로 사용할경우 컴포넌트에서도 참조 방식이 바뀌게 된다.
ExpenseItem.js // 컴포넌트
<div className="expense-item">
<div>{props.expenses.date.toISOString}</div>
<h2>{props.expenses.title}</h2>
</div>
왜냐하면 개별적으로 props를 받은게 아니라 하나의 객체(expenses 라는 이름)을 받았으므로 내가 임의로 정한 객체의 이름을 한번더 사용해 참조 해야한다.
자바스크립트에 내장된 기능을 이용해 prop를 전달받을 수 있다.
ExpenseItem.js
function ExpenseItem({date,title,amount})
// 위와 같은 방식으로 객체 구조 분해 할당을 사용해 prop를 전달받는다.
// 이런식으로 사용할경우 참조방식도 단지 바로 date나 title만 써주면 끝이다.
<div className="expense-item">
<div>{date.toISOString}</div>
<h2>{title}</h2>
</div>
사용자가 보기 편한 인터페스의 날짜를 구현하고자 한다.
ExpenseItem.js
import "./ExpenseItem.css";
function ExpenseItem(props) {
return (
<div className="expense-item">
<div>
<div>{props.date.toLocaleString('en-US', {month: 'long'})}</div>
<div>{props.date.toLocaleString('en-US', {day : '2-digit'})}</div>
<div>{props.date.getFullYear()}</div>
</div>
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</div>
);
}
export default ExpenseItem;
위 방식으로 JSX 코드 내에서 중괄호로 계산식을 사용해 하나하나 표기할 수도 있다.
하지만 위 방식으로 코드를 지속적으로 사용한다면 추후 가독성면에서 매우 나빠질 수 있으므로 지양해야한다.
다른 방식으로 사용해보자.
JSX 코드 외부에 헬퍼 변수나 상수를 설정할 수 있고 JSX 코드 내에서 헬퍼 변수나 상수를 단지 중괄호를 열고 설정한것을 사용하기만 하면 된다.
ExpenseItem.js
import "./ExpenseItem.css";
function ExpenseItem(props) {
const month = props.date.toLocaleString('en-US', {month: 'long'}) ;
const day = props.date.toLocaleString('en-US', {day : '2-digit'})
const year = props.date.getFullYear()
return (
<div className="expense-item">
<div>
<div>{month}</div>
<div>{day}</div>
<div>{year}</div>
</div>
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</div>
);
}
export default ExpenseItem;
ExpenseItem.js
import "./ExpenseItem.css";
function ExpenseItem(props) {
const month = props.date.toLocaleString('en-US', {month: 'long'}) ;
const day = props.date.toLocaleString('en-US', {day : '2-digit'})
const year = props.date.getFullYear()
return (
<div className="expense-item">
<div>
<div>{month}</div>
<div>{day}</div>
<div>{year}</div>
</div>
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</div>
);
}
export default ExpenseItem;
위의 코드를 보니 컴포넌트가 점점 복잡해지는것을 볼 수 있다.
보기 깔끔하고 유지보수면에서 좋아질만한 요소가 없을까?
ExpenseItem.js 컴포넌트를 두개의 컴포넌트로 분할하는것을 한다면?
ExpenseItem.js
import ExpenseDate from "./ExpenseDate";
import "./ExpenseItem.css";
function ExpenseItem(props) {
return (
<div className="expense-item">
{/* ExpenseDate 컴포넌트 사용및 컴포넌트에게 date에대한 데이터 전달하기 */}
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</div>
);
}
export default ExpenseItem;
ExpenseDate.js
import "./ExpenseDate.css";
{/* ExpenseItem으로부터 전달받은 데이터(date)를 props로 사용하기 */
function ExpenseDate(props) {
const month = props.date.toLocaleString('en-US', {month: 'long'});
const day = props.date.toLocaleString('en-US', {day : '2-digit'});
const year = props.date.getFullYear();
return(
<div className="expense-date">
<div className="expense-date__month">{month}</div>
<div className="expense-date__year">{day}</div>
<div className="expense-date__day">{year}</div>
</div>
);
}
export default ExpenseDate;
위 코드에서 볼 수 있듯이 props를 통해 받은 데이터들은 또다시 다른 컴포넌트에게
props를 통해 데이터를 전달할 수 있다. 이를 통하여 유지보수에 편리한 컴포넌트들을 구축하여 설계할 수 있다. 코드들을 정리하여 그림으로 정리하면 다음과 같다.
컴포지션 : 작은 구성요소부터 시작해서 사용자 인터페이스를 구축하는 방식.
A 컴포넌트와 B 컴포넌트에 대해 공통된 외곽 껍데기를 만든다면 어떨까?(래퍼)
중복된 코드를 줄이고 효율적인 작업이 가능할것 같다.
그러한 래퍼를 Card(Card.jss)라고 이름을 짓고 간단한 CSS(Card.css)도 추가한다.
Card.css
export default Card;
.card{
border-radius: 12px;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.25);
}
Card.js
import './Card.css';
function Card(props) {
const classes = 'card ' + props.className; {/* 1 */}
return <div className={classes}>{props.children}</div>; {/* 2 */}
}
1번에서 'card '+ props.className의 의미는
‘card ' : Card.css파일에 있는 .card 클래스를 기본으로 설정합니다.
// card 뒤에 스페이스를 해주는 이유는 CSS에서 클래스를 여러개 지정하고싶을때 스페이스로 구분하기 때문이다.
props.className : 사용자 지정 컴포넌트 외부에서 오는 props에 대한 클래스를 들여와 추가합니다.
2번에서 children은 예약어로 props.children을 써주지 않는다면 사용자 지정 컴포넌트의 시작 태그와 종료 태그 사이에 있는 콘텐츠가 표시되지 않을것이다.
ExpenseItem.js
function ExpenseItem(props) {
return (
<Card className="expense-item">
{/* App.js에서 받은 props.date에 대한 데이터를 다시 ExpenseDate.js에 props로 전달*/}
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</Card>
);
}
예를 들어 위 코드에서 Card컴포넌트를 사용하려고 할때 props.children을 써주지 않는다면 가장 바깥쪽에 있는 Card 영역만 표시되고 안쪽영역의 div 영역들은 모두 표시되지 않는다.