从设计稿到实现React应用(分类数据处理)
1. 确定设计稿和数据
设计稿:
数据:
[ {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"} ];
2. 将设计稿UI分组件层级
组件单一功能原则:
即一个组件实现一个功能。
图中,一个颜色是一种组件,按照嵌套关系,可以分为:
FilterableProductTable:
SearchBar:
ProductTable:
ProductCategoryRow
ProductRow
3. 确定需要的state值的个数
原则:
1. 值不是来自与props
2. 值不是一成不变
3. 值不能由其他state和props求得
该示例中,需要数据源的地方有:
1. 查询框的文本 // 会变化;state.filterText
2. 复选框的状态 // 会变化;state.isStock
3. 所有的产品列表数据 // 通过props传入; 无state
4. 过滤后的产品列表数据 //通过1,2,3获得; 无state
4. 确定state的位置-状态提升
通过分析state对应的值作用的范围,state应该位于所有作用区域的父组件;
才能共享state。
从功能分析,1,2的状态会影响列表展示;则其应该位于查询框和列表的父组件;
即组件FilterableProductTable中。
3的状态会影响列表分类ProductCategoryRow和列表项ProductRow,则应该放在他们的父组件;
即组件ProductTable中。
5. 添加反向数据流
即通过触发子组件的事件监听,调用父组件传递过去的函数,从而改变父组件的state的值。
因为state的状态提升,而state又是私有状态,只能通过state所处的组件的函数进行修改。
6. 从下向上编写代码
/* * @Author: LyraLee * @Date: 2019-11-08 09:01:58 * @LastEditTime: 2019-11-20 17:32:14 * @Description: 分类数据处理 */ 'use strict' const products = [ {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"}, {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"}, {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"}, {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"}, {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"}, {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"} ]; function ProductRow(props) { const product = props.product; const { name } = product; const renderName = !product.stocked ? <span style={{color: 'red'}}>{name}</span> : name return ( <tr> <td>{renderName}</td> <td>{product.price}</td> </tr> ) } function ProductCategoryRow(props) { const product = props.product; return( <tr> <th colSpan="2">{product.category}</th> </tr> ) } function ProductTable(props) { const { filterText, isStockedOnly, products } = props; // 处理数据的方法!!!需要分类的属性是category, // 如果数据不是按照该属性排序,那先将其按照category的字段顺序 let lastCategory = null; let rows = [] products.sort((a,b) => a.category.localeCompare(b.category)) .forEach(product => { if (!product.name.includes(filterText)) { return; } if (isStockedOnly && !product.stocked) { return; } if (product.category !== lastCategory) { rows.push(<ProductCategoryRow key={product.category} product={product} />) } rows.push( <ProductRow key={product.name} product={product} /> ) lastCategory = product.category; }); return ( <table> <thead> <tr> <th>name</th> <th>price</th> </tr> </thead> <tbody> {rows} </tbody> </table> ) } function SearchBar(props) { function handleChange (e) { const { name, type } = e.target; const value = type === 'checkbox' ? e.target.checked : e.target.value; props.handleChange(name, value); } const { filterText, isStockedOnly } = props; return( <div> <input name="filterText" type="text" value={filterText} onChange={handleChange} placeholder="search..." /> <p> <input name="isStockedOnly" type="checkbox" checked={isStockedOnly} onChange={handleChange} />{' '} Only show products in stock </p> </div> ) } class FileterableProductTable extends React.Component { constructor(props) { super(props); this.state = { filterText: '', isStockedOnly: false } } handleChange = (name, value) => { this.setState({ [name]: value }) } render() { const { filterText, isStockedOnly } = this.state; const { products } = this.props; return( <React.Fragment> <SearchBar filterText={filterText} isStockedOnly={isStockedOnly} handleChange={this.handleChange} /> <ProductTable filterText={filterText} isStockedOnly={isStockedOnly} products={products} /> </React.Fragment> ) } } ReactDOM.render(<FileterableProductTable products={products} />, root);