第三步:找出最少(但完整)的 UI State
思考应用程式最少需要哪些可变的 state,关键是DRY(避免重複代码原则)DRY(避免重複代码原则):在软体开发中,减少重複的资讯,简单来说就是不要写重複的程式,可以把共同的程式取出来统一放到一个地方进行呼叫而不是相同的程式写很多次。须注意与思考的三个点:
1.这个资料是从父层透过 props 传下来的吗?如果是的话,那它很可能不是 state。
2.这个资料是否一直保持不变呢?如果是的话,那它很可能不是 state。
3.是否可以根据 component 中其他的 state 或 prop 来计算这个资料呢?如果是的话,那它一定不是 state。
商品数据库是被当作 prop 往下传的,所以它不是 state。而搜寻关键字和 checkbox会随时间而改变,也不能从其他东西中被计算出来,所以是state。
第四步:找出你的 State 应该在哪里
指出每个根据 state 来 render 某些东西的 component。找出一个共同拥有者 component(在层级中单一一个需要 state 的、在所有的 component 之上的 component)。应该拥有 state 的会是共同拥有者 component 或另一个更高层级的 component。如果你找不出一个应该拥有 state 的 component 的话,那就建立一个新的 component 来保持 state,并把它加到层级中共同拥有者 component 之上的某处。ProductTable 需要根据 state 来筛选产品列表,而 SearchBar 需要展示搜寻关键字和 checkbox 的 state。这两个 component 的共同拥有者 component 是 FilterableProductTable。FilterableProductTableSearchBar (搜寻关键字和 checkbox)ProductTable (筛选产品列表)ProductCategoryRowProductRow流程图 :
FilterableProductTable:
放置State的初始化,并将state透过props传递给子层(SearchBar / ProductTable)
class FilterableProductTable extends React.Component { constructor(props) { super(props); //state初始化 this.state = { filterText: '', inStockOnly: false }; } render() { return ( <div> {/* 将初始化的state利用props传递给Sub Component */} <SearchBar filterText={this.state.filterText} inStockOnly={this.state.inStockOnly} /> <ProductTable products={this.props.products} filterText={this.state.filterText} inStockOnly={this.state.inStockOnly} /> </div> ); }}
SearchBar :
接收父层的数据库资料与state,并将牠放入html中
class SearchBar extends React.Component { render() { const filterText = this.props.filterText; const inStockOnly = this.props.inStockOnly; return ( <form> {/* 将父层传递的State放入HTML元件中,设定初始状态 */} <input type="text" placeholder="Search..." value={filterText} /> <p> <input type="checkbox" checked={inStockOnly} /> {' '} Only show products in stock </p> </form> ); } }
ProductTable:
接收父层的数据库资料与state,并加入与html之间互动的判断(表单填入资料与数据库比对、checkedBox和库存状态)
class ProductTable extends React.Component { render() { const filterText = this.props.filterText; const inStockOnly = this.props.inStockOnly; const rows = []; let lastCategory = null; /* 将数据库阵列内每个元素传入function function判断: 1.判断输入表单的name是否是数据库中的name,若不是(-1)则跳出function 2.判断checkbox是否有被勾选(true)与是否没有库存(!(stocked=true) = false) 3.判断category是否与前一个一样,若不是的便会将"ProductCategoryRow"加入到row阵列中并把"category" 与 "key"利用props传给子层;若一样便会把"ProductRow"加入到row阵列并将"product" 与 "key"利用props传给子层 */ this.props.products.forEach((product) => { //indexOf() : 回传给定元素于阵列中第一个被找到之索引,若不存在于阵列中则回传-1 if (product.name.indexOf(filterText) === -1) { return; } else if (inStockOnly && !product.stocked) { return; } else 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> ); } }