JSON API와 목업을 디자이너로부터 하기와 같이 전달이 되었다.
[
{category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
{category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
{category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
{category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
{category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
{category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];
1) FilterableProductTable: Mock up 전체
2) SearchBar: user input component
3) ProductTable: user input을 기반한 data collection filtering component
4) ProductCategoryRow: category header component
5) ProductRow: product row component
Name
과 Price
를 포함한 table header component 가 없는 이유는 데이터를 위한 독립된 component를 생성할지 생성하지 않을지는 선택이기 때문이다. ProductTable
의 책임인 data collection이 렌더링의 일부이기 때문에 ProductTable
을 따로 작성한다. 만약 헤더가 복잡해진다면 ProductTableHeader
component는 만들어서 분리하는 것이 더 합리적이다.
|-- FilterableProductTable
|-- SearchBar
|-- ProductTable
|-- ProductCategoryRow
|-- ProductRow
class ProductCategoryRow extends React.Component {
render() {
const category = this.props.category;
return (
<tr>
<th colSpan="2">
{category}
</th>
</tr>
);
}
}
class ProductRow extends React.Component {
render() {
const product = this.props.product;
const name = product.stocked ?
product.name :
<span style={{color: 'red'}}>
{product.name}
</span>;
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
}
class ProductTable extends React.Component {
render() {
const rows = [];
let lastCategory = null;
this.props.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>
);
}
}
class SearchBar extends React.Component {
render() {
return (
<form>
<input type="text" placeholder="Search..." />
<p>
<input type="checkbox" />
{' '}
Only show products in stock
</p>
</form>
);
}
}
class FilterableProductTable extends React.Component {
render() {
return (
<div>
<SearchBar />
<ProductTable products={this.props.products} />
</div>
);
}
}
const PRODUCTS = [
{category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
const root = ReactDOM.createRoot(document.getElementById('container'));
root.render(<FilterableProductTable products={PRODUCTS} />);
구현 과정을 나눈다.
데이터 모델을 가지고 UI를 렌더링은 되지만 아무 동작도 없는 버전을 만들어본다.
-> 과정을 나누는 것은 좋으나 정적 버전을 만드는 것은 생각은 적게 필요하지만 타이핑이 많으며, 상호작용을 만드는 것은 많은 생각을 필요로 하지만 타이밍은 적게 필요로 하기 때문이다.
state
가 없다. state
는 오직 시간이 지남에 따라 데이터가 바뀌는 것에 사용한다.root.render()
를 다시 호출하여 UI가 업데이트가 된다. 업데이트된 UI는 어디에서 변경해야 되는지 알 수 있다.UI를 상호작용하게 만들기 위해선 데이터 모델을 변경할 수 있는 방법이 필요하다. 이것을 위해 state를 이용한다.
어떤 component가 state를 변경하거나 소유할지 설정을 해야된다.
하단 컴포넌트에서 state를 업데이트 할 수 있어야된다.
양방향 데이터 방인딩을 사용할 수 있다. 타이핑을 많지만, 데이터 흐름을 명시적으로 보이게 만들어 프로그램의 흐름을 알 수 있다.
user의 action을 통해 상태가 변해야된다. callback
함수를 props로 넘겨 state가 업데이트되어야 할 때마다 호출이 되도록 한다. onChange
나 onClick
과 같은 이벤트를 사용해서 이벤트에 대한 알림을 받을 수 있다. 전달된 callback
은 setState()
를 호출하고 앱이 업데이트가 될 것이다.