React 設計哲學

React 最棒的部分之一是引導咱們思考如何構建一個應用。在這篇文檔中,咱們將會經過 React 構建一個可搜索的產品數據表格來更深入地領會 React 哲學。 本文參考官方文檔文檔連接html

  • 從產品應用思考技術實現

    • 例如咱們有這樣一個設計稿

設計稿
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"}
];
複製代碼

設計稿劃分

  1. FilterableProductTable (橙色): 是整個示例應用的總體
  2. SearchBar (藍色): 接受全部的用戶輸入
  3. ProductTable (綠色): 展現數據內容並根據用戶輸入篩選結果
  4. ProductCategoryRow (天藍色): 爲每個產品類別展現標題
  5. ProductRow (紅色): 每一行展現一個產品

抽象層級

  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow

如今咱們用代碼展示react

把架子搭建出來json

量太多 詳見 codepen codepenredux

肯定狀態 [state]

什麼是狀態? 咱們能夠吧用戶是|| 否點擊 ,用戶的search且這些都是動態的狀態,動態的狀態對應這上圖最小化UI的動做, 對應上圖 就是根據藍框的狀態來變化綠框 簡單的概況就是: 數據驅動視圖this

state放置在那一層

注意: 單從React 組件設計考慮,不涉及context redux mobx 等複雜跨組件交互,spa

  • 找到根據這個 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 中被監聽的數據,從而改變視圖code

最後貼出完整代碼

codepen在線查看orm

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')
);

複製代碼
相關文章
相關標籤/搜索