React 最棒的部分之一是引导我们思考如何构建一个应用。在这篇文档中,我们将会通过 React 构建一个可搜索的产品数据表格来更深刻地领会 React 哲学。 本文参考官方文档文档链接
json
[ {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"} ]; FilterableProductTable (橙色): 是整个示例应用的整体SearchBar (蓝色): 接受所有的用户输入ProductTable (绿色): 展示数据内容并根据用户输入筛选结果ProductCategoryRow (天蓝色): 为每一个产品类别展示标题ProductRow (红色): 每一行展示一个产品现在我们用代码展现
把架子搭建出来
量太多 详见 codepen codepen
什么是状态? 我们可以吧用户是|| 否点击 ,用户的search且这些都是动态的状态,动态的状态对应这上图最小化UI的动作, 对应上图 就是根据蓝框的状态来变化绿框 简单的概况就是: 数据驱动视图
注意: 单从React 组件设计考虑,不涉及context redux mobx 等复杂跨组件交互,
找到根据这个 state 进行渲染的所有组件。找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。该共同所有者组件或者比它层级更高的组件应该拥有该 state。如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。根据以上策略重新考虑我们的示例应用:
ProductTable 需要根据 state 筛选产品列表。SearchBar 需要展示搜索词和复选框的状态。他们的共同所有者是 FilterableProductTable。因此,搜索词和复选框的值应该很自然地存放在 FilterableProductTable 组件中。很好,我们已经决定把这些 state 存放在 FilterableProductTable 组件中。首先,将实例属性 this.state = {filterText: ‘’, inStockOnly: false} 添加到 FilterableProductTable 的 constructor 中,设置应用的初始 state;接着,将 filterText 和 inStockOnly 作为 props 传入 ProductTable 和 SearchBar;最后,用这些 props 筛选 ProductTable 中的产品信息,并设置 SearchBar 的表单值。我们知道react 是一个单向数据流 也就是数据变化视图变化,但是视图中的input变化我们的数据还没变化,这就需要我们来实现,通过 fn 来触发 改变state 中被监听的数据,从而改变视图
codepen在线查看
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 filterText = this.props.filterText; const inStockOnly = this.props.inStockOnly; const rows = []; let lastCategory = null; this.props.products.forEach((product) => { if (product.name.indexOf(filterText) === -1) { return; } if (inStockOnly && !product.stocked) { return; } 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 { constructor(props) { super(props); this.handleFilterTextChange = this.handleFilterTextChange.bind(this); this.handleInStockChange = this.handleInStockChange.bind(this); } handleFilterTextChange(e) { this.props.onFilterTextChange(e.target.value); } handleInStockChange(e) { this.props.onInStockChange(e.target.checked); } render() { return ( <form> <input type="text" placeholder="Search..." value={this.props.filterText} onChange={this.handleFilterTextChange} /> <p> <input type="checkbox" checked={this.props.inStockOnly} onChange={this.handleInStockChange} /> {' '} Only show products in stock </p> </form> ); } } class FilterableProductTable extends React.Component { constructor(props) { super(props); this.state = { filterText: '', inStockOnly: false }; this.handleFilterTextChange = this.handleFilterTextChange.bind(this); this.handleInStockChange = this.handleInStockChange.bind(this); } handleFilterTextChange(filterText) { this.setState({ filterText: filterText }); } handleInStockChange(inStockOnly) { this.setState({ inStockOnly: inStockOnly }) } render() { return ( <div> <SearchBar filterText={this.state.filterText} inStockOnly={this.state.inStockOnly} onFilterTextChange={this.handleFilterTextChange} onInStockChange={this.handleInStockChange} /> <ProductTable products={this.props.products} filterText={this.state.filterText} inStockOnly={this.state.inStockOnly} /> </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'} ]; ReactDOM.render( <FilterableProductTable products={PRODUCTS} />, document.getElementById('container') );