지금부터 React에서 UI를 구현하는 일반적인 5단계를 설명한다
(취향에 따라 ProductTableHeader 컴포넌트도 추가할 수 있다)
이제 목업에서 컴포넌트를 식별했으므로 계층 구조를 정렬한다
- FilterableProductTable
- SearchBar
- ProductTable
- ProductCategoryRow
- ProductRow
컴포넌트 계층 구조가 완성되었으니 이제 앱을 구현
가장 간단한 접근은 상호작용을 추가하지 않고 데이터 모델에서 UI렌더링 하는 버전을 만드는 것
정적 버전을 먼저 만든 다음 상호작용(interactivity)을 별도로 추가하는것이 쉽다
앱의 정적 버전을 만들기 위해서는 다른 컴포넌트를 재사용하는 component를 만들고 props를 사용하여 데이터 전달
state는 상호작용, 시간이 지남에 따라 변하는 데이터에만 사용!
function ProductCategoryRow({ category }) {
return (
<tr>
<th colSpan="2">
{category}
</th>
</tr>
);
}
function ProductRow({ product }) {
const name = product.stocked ? product.name :
<span style={{ color: 'red' }}>
{product.name}
</span>;
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
function ProductTable({ products }) {
const rows = [];
let lastCategory = null;
products.forEach((product) => {
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category} />
);
}
rows.push(
<ProductRow
product={product}
key={product.name} />
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
function SearchBar() {
return (
<form>
<input type="text" placeholder="Search..." />
<label>
<input type="checkbox" />
{' '}
Only show products in stock
</label>
</form>
);
}
function FilterableProductTable({ products }) {
return (
<div>
<SearchBar />
<ProductTable products={products} />
</div>
);
}
const PRODUCTS = [
{category: "Fruits", price: "$1", stocked: true, name: "Apple"},
{category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"},
{category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"},
{category: "Vegetables", price: "$2", stocked: true, name: "Spinach"},
{category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"},
{category: "Vegetables", price: "$1", stocked: true, name: "Peas"}
];
export default function App() {
return <FilterableProductTable products={PRODUCTS} />;
}
그럼 이 앱의 모든 데이터 조각을 생각해보고, 그중 무엇이 state인지 식별해보자
1. 제품의 원본 목록
2. 사용자가 입력한 검색어
3. 체크박스의 값
4. 필터링된 제품 목록
state가 되지 않는 조건
1. 시간이 지나도 변하지 않나요?
2. 부모로부터 props를 통해 전달되나요?
3. 컴포넌트의 기존 state 또는 props를 가지고 계산할 수 있나요?
1. 제품의 원본 목록
=> 제품의 원본 목록은 props로 전달되었으므로 state가 아닙니다.
2. 사용자가 입력한 검색어
=> 시간에 따라 변하기 때문에 state입니다.
3. 체크박스의 값
=> 시간에 따라 바뀌고, 다른 것으로 부터 계산할 수 없으므로 state입니다
4. 필터링된 제품 목록
=> 원복 목록으로 부터 검색어 및 체크박스 값을 조합하여 계산할 수 있으므로 state가 아닙니다
즉 검색어와 체크박스의 값만 state이다
1. 해당 state를 기반으로 렌더링하는 모든 컴포넌트를 찾자
2. 가장 가까운 공통 상위 컴포넌트, 즉 계층상 그 state의 영향을 받는 모든 컴포넌트들의 위에 있는 컴포넌트를 찾자
3. state가 어디 위치할지 결정하자
4. 대게 공통 부모 state를 그대로 둘 수 있다
5. 혹은 공통 부모보다 더 상위 컴포넌트에 state를 둘 수 있다
6. state를 소유할 적절한 컴포넌트가 없다면, 상위에 추가하자
// 두 개의 state 변수를 추가
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
// filterText와 inStockOnly를 ProductTable과 SearchBar에 props로 전달
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly} />
<ProductTable
products={products}
filterText={filterText}
inStockOnly={inStockOnly} />
</div>
느낀점
참고: https://react-ko.dev/learn/thinking-in-react#start-with-the-mockup