學習React思想

React和React-native在編程的思想上是完全一樣的蔓肯,所以要寫出好的RN代碼吱殉,學學React的思想很有必要递礼。本文是學習facebook官方文章《Thinking in React》的記錄岖圈,包括關(guān)鍵點的翻譯和自己的理解鹦牛。

英文原文地址
這片文章大部分是翻譯和簡化搞糕,小部分是自己理解。

先看看需求

假設(shè)我們需要實現(xiàn)這樣一個可過濾的商品列表

draft.png

而且我們有這樣的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"}
];

將設(shè)計稿分解成Component層次

使用矩形線框來確定Component和子Component曼追。這里要遵循單一職責原則窍仰,一個Component只做一件事情。

分解后的設(shè)計稿
  1. FilterableProductTable-橙色:根節(jié)點
  2. SearchBar-藍色:處理用戶輸入
  3. ProductTable-綠色:根據(jù)用戶輸入展現(xiàn)商品列表
  4. ProductCategoryRow-藍綠色:顯示分類名稱
  5. ProductRow-紅色:顯示單個商品信息

如下為樹狀結(jié)構(gòu)

FilterableProductTable

|- SearchBar

|- ProductTable

|- ProductCategoryRow

|- ProductRow

完成靜態(tài)頁面

var ProductCategoryRow = React.createClass({
  render: function() {
    return (<tr><th colSpan="2">{this.props.category}</th></tr>);
  }
});

var ProductRow = React.createClass({
  render: function() {
    var name = this.props.product.stocked ?
      this.props.product.name :
      <span style={{color: 'red'}}>
        {this.props.product.name}
      </span>;
    return (
      <tr>
        <td>{name}</td>
        <td>{this.props.product.price}</td>
      </tr>
    );
  }
});

var ProductTable = React.createClass({
  render: function() {
    var rows = [];
    var lastCategory = null;
    this.props.products.forEach(function(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>
    );
  }
});

var SearchBar = React.createClass({
  render: function() {
    return (
      <form>
        <input type="text" placeholder="Search..." />
        <p>
          <input type="checkbox" />
          {' '}
          Only show products in stock
        </p>
      </form>
    );
  }
});

var FilterableProductTable = React.createClass({
  render: function() {
    return (
      <div>
        <SearchBar />
        <ProductTable products={this.props.products} />
      </div>
    );
  }
});


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

需要注意完成靜態(tài)頁面的時候不要使用state礼殊,因為state都是用于保存用戶交互結(jié)果的驹吮。而且每個組件都只有render()方法,根組件FilterableProductTable將通過props獲得數(shù)據(jù)模型晶伦,這種單向數(shù)據(jù)流使得React能很好的模塊化钥屈,響應(yīng)也很快。
stateprops是React中的兩種數(shù)據(jù)模型坝辫,理解兩者的區(qū)別很重要篷就。簡單的理解props是外部傳遞進來的數(shù)據(jù),state是用戶和組件交互時產(chǎn)生的數(shù)據(jù)近忙。更加詳細的說明可以參考Facebook的另一篇官方文章

小而全的state

這個例子中的數(shù)據(jù)包括

  1. 商品列表
  2. 用戶輸入的搜索關(guān)鍵字
  3. 復選框的狀態(tài)
  4. 過濾后的商品列表

判斷數(shù)據(jù)是否為state的標準

  1. 外部傳入的不是state
  2. 不會變化的不是state
  3. 可以通過其他stateprops計算得到的不是state

根據(jù)以上原則竭业,是state的為

  1. 用戶輸入的搜索關(guān)鍵字
  2. 復選框的狀態(tài)

state的作用域

var ProductCategoryRow = React.createClass({
  render: function() {
    return (<tr><th colSpan="2">{this.props.category}</th></tr>);
  }
});

var ProductRow = React.createClass({
  render: function() {
    var name = this.props.product.stocked ?
      this.props.product.name :
      <span style={{color: 'red'}}>
        {this.props.product.name}
      </span>;
    return (
      <tr>
        <td>{name}</td>
        <td>{this.props.product.price}</td>
      </tr>
    );
  }
});

var ProductTable = React.createClass({
  render: function() {
    var rows = [];
    var lastCategory = null;
    this.props.products.forEach(function(product) {
      if (product.name.indexOf(this.props.filterText) === -1 || (!product.stocked && this.props.inStockOnly)) {
        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;
    }.bind(this));
    return (
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Price</th>
          </tr>
        </thead>
        <tbody>{rows}</tbody>
      </table>
    );
  }
});

var SearchBar = React.createClass({
  render: function() {
    return (
      <form>
        <input type="text" placeholder="Search..." value={this.props.filterText} />
        <p>
          <input type="checkbox" checked={this.props.inStockOnly} />
          {' '}
          Only show products in stock
        </p>
      </form>
    );
  }
});

var FilterableProductTable = React.createClass({
  getInitialState: function() {
    return {
      filterText: '',
      inStockOnly: false
    };
  },

  render: function() {
    return (
      <div>
        <SearchBar
          filterText={this.state.filterText}
          inStockOnly={this.state.inStockOnly}
        />
        <ProductTable
          products={this.props.products}
          filterText={this.state.filterText}
          inStockOnly={this.state.inStockOnly}
        />
      </div>
    );
  }
});


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

確定state的作用域就是要確定哪個Component應(yīng)該持有這個state,這里有幾個步驟及舍。

  1. 找到所有render()中會用到這個stateComponent
  2. 找到一個Component包括包含了所有1中的Component
  3. 2中的Component或者它的上級Component持有這個state
  4. 如果你找不到一個有意義的Component持有這個state未辆,就在這些節(jié)點之上創(chuàng)造一個新的Component來持有這個state

在我們的例子中

  1. ProductTable需要根據(jù)狀態(tài)來確定顯示的列表項目,SearchBar也需要狀態(tài)來顯示搜索文本和復選框狀態(tài)
  2. 所以包含兩者的公共ComponentFilterableProductTable
  3. FilterableProductTable持有state也是有明確意義的

所以我們在FilterableProductTable中持有state锯玛,然后將state作為props傳遞給ProductTableSearchBar

反向數(shù)據(jù)流

ar ProductCategoryRow = React.createClass({
  render: function() {
    return (<tr><th colSpan="2">{this.props.category}</th></tr>);
  }
});

var ProductRow = React.createClass({
  render: function() {
    var name = this.props.product.stocked ?
      this.props.product.name :
      <span style={{color: 'red'}}>
        {this.props.product.name}
      </span>;
    return (
      <tr>
        <td>{name}</td>
        <td>{this.props.product.price}</td>
      </tr>
    );
  }
});

var ProductTable = React.createClass({
  render: function() {
    var rows = [];
    var lastCategory = null;
    this.props.products.forEach(function(product) {
      if (product.name.indexOf(this.props.filterText) === -1 || (!product.stocked && this.props.inStockOnly)) {
        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;
    }.bind(this));
    return (
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Price</th>
          </tr>
        </thead>
        <tbody>{rows}</tbody>
      </table>
    );
  }
});

var SearchBar = React.createClass({
  handleChange: function() {
    this.props.onUserInput(
      this.refs.filterTextInput.value,
      this.refs.inStockOnlyInput.checked
    );
  },
  render: function() {
    return (
      <form>
        <input
          type="text"
          placeholder="Search..."
          value={this.props.filterText}
          ref="filterTextInput"
          onChange={this.handleChange}
        />
        <p>
          <input
            type="checkbox"
            checked={this.props.inStockOnly}
            ref="inStockOnlyInput"
            onChange={this.handleChange}
          />
          {' '}
          Only show products in stock
        </p>
      </form>
    );
  }
});

var FilterableProductTable = React.createClass({
  getInitialState: function() {
    return {
      filterText: '',
      inStockOnly: false
    };
  },

  handleUserInput: function(filterText, inStockOnly) {
    this.setState({
      filterText: filterText,
      inStockOnly: inStockOnly
    });
  },

  render: function() {
    return (
      <div>
        <SearchBar
          filterText={this.state.filterText}
          inStockOnly={this.state.inStockOnly}
          onUserInput={this.handleUserInput}
        />
        <ProductTable
          products={this.props.products}
          filterText={this.state.filterText}
          inStockOnly={this.state.inStockOnly}
        />
      </div>
    );
  }
});


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

這一部分在原文中稍微有些復雜咐柜。簡而言之:stateFilterableProductTable中兼蜈,但是真正的交互發(fā)生在SearchBar的字元素中,所以需要從子向父傳遞信息拙友。這里主要是通過回調(diào)機制實現(xiàn)的为狸。

  1. FilterableProductTable通過props.onUserInput傳一個回調(diào)函數(shù)handleUserInputSearchBar
  2. 在文本或者復選狀態(tài)發(fā)生變化時遗契,通過onChange指定調(diào)用SearchBarhandleChange
  3. handleChange通過refs獲取文本和復選框狀態(tài)辐棒,并執(zhí)行回調(diào)函數(shù)。refs的機制文中并沒有詳述牍蜂。
  4. handleUserInput中改變state漾根,并更新頁面
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鲫竞,隨后出現(xiàn)的幾起案子辐怕,更是在濱河造成了極大的恐慌,老刑警劉巖从绘,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寄疏,死亡現(xiàn)場離奇詭異,居然都是意外死亡顶考,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門妖泄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驹沿,“玉大人,你說我怎么就攤上這事蹈胡≡荆” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵罚渐,是天一觀的道長却汉。 經(jīng)常有香客問我,道長荷并,這世上最難降的妖魔是什么合砂? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮源织,結(jié)果婚禮上翩伪,老公的妹妹穿的比我還像新娘。我一直安慰自己谈息,他們只是感情好缘屹,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侠仇,像睡著了一般轻姿。 火紅的嫁衣襯著肌膚如雪犁珠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天互亮,我揣著相機與錄音犁享,去河邊找鬼。 笑死胳挎,一個胖子當著我的面吹牛饼疙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播慕爬,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼窑眯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了医窿?” 一聲冷哼從身側(cè)響起磅甩,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎姥卢,沒想到半個月后卷要,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡独榴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年僧叉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棺榔。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓶堕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出症歇,到底是詐尸還是另有隱情郎笆,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布忘晤,位于F島的核電站宛蚓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏设塔。R本人自食惡果不足惜凄吏,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闰蛔。 院中可真熱鬧竞思,春花似錦、人聲如沸钞护。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽难咕。三九已至课梳,卻和暖如春距辆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背暮刃。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工跨算, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人椭懊。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓诸蚕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親氧猬。 傳聞我的和親對象是個殘疾皇子背犯,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Learn from React 官方文檔 一、Rendering Elements 1. Rendering a...
    恰皮閱讀 2,664評論 2 3
  • React的思想 在我看來, React 是較早使用 JavaScript 構(gòu)建大型盅抚、快速的 Web 應(yīng)用程序的技...
    KavinZhou閱讀 585評論 0 4
  • 最近看了一本關(guān)于學習方法論的書漠魏,強調(diào)了記筆記和堅持的重要性。這幾天也剛好在學習React妄均,所以我打算每天堅持一篇R...
    gaoer1938閱讀 1,680評論 0 5
  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,064評論 2 35
  • 假設(shè)我們要創(chuàng)建一個應(yīng)用柱锹,如何用react的思維進行創(chuàng)建呢?本例是react官網(wǎng)的例子丰包,主要是進行更詳細的敘述和細節(jié)...
    Aleph_Zheng閱讀 7,435評論 6 1