Thinking in React(翻译)

下面是React官方文档中的Thinking inReact文章的翻译,第一次翻译英文的文章,肯定有非常多不对的地方,还望多多包涵。

原文地址:https://facebook.github.io/react/docs/thinking-in-react.html

原文開始

---------------------------------------------我是分隔符------------------------------------------

Thinking in React

by Pete Hunt

React是什么?我的看法是。它是用javascript创建大型。高速web应用的首要方式。Fackbook和Instagram为我们做了非常好的測试。

React的当中一个很厉害的部分就是它是让你在构建你的应用时如何去思考的。接下来,我会带你体验一下如何使用React去构建一个有搜索功能的产品数据表。

Start with a mock

想象一下我们已经有了JSON数据的API和设计师给的站点模型。我们的设计师明显不怎么样由于他给的模型是这种:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

我们的JSON接口返回的数据是这种:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

Step 1: Break the UI into a component hierarchy(把UI切割成组件层)

你要做的第一件事就是在组件周围画上盒子而且给它们取一个名字。有过你和网页设计师一起工作。那他们可能已经做过这个工作了,去和他们谈谈。

他们的

photoshop layer的名字,最后应该就会成为你的React组件的名字。

可是你怎么知道谁应该成为一个单独组件呢?用这种方法就能够了:依据你是否须要去创建一个新的方法或者对象就能够决定是否是一个组件。这就是单一职责原则,原则就是:一个组件在理想状态下仅仅做一件事情。假设之后须要扩展。那么就把它切割成子组件。

假设你常常向用户展示JSON数据模型。你就会发现假设你的模型建立正确的话。你的UI(也就是你的组件结构)的布局就不会有问题。由于UI和数据模型都是依靠同样的信息结构,这也就以为这把你的UI分隔成组件是非常寻常的。

去把UI分隔成代表单块数据模型的组件吧。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

你能够看到在我们简单地应用中有五个组件,

1.FilterableProductTable(橙色): 包括了应用这个总体。

2.SearchBar(蓝色):接收了用户输入

3.ProductTable(绿色):根据用户输入展示和过滤数据

4.ProductCategoryRow(天蓝色)区域标题

5.ProductRow(红色):展示每一个产品的行

看一下ProductTable,你就会发现表头(包括‘Names’和‘Price’标签)不是它自己的组件。这是一种偏好,对此有非常多的争论。在这个样例中。我把它放在了ProductTable里边,是由于它是渲染ProductTable数据集中的一部分。然而,假设这个表头变复杂了(比如:我们增加了排序功能),它就得建立自己的ProductTableHeader元素。如今我们已经确认了我们用例中的组件,那就把它们层次化。组件中的小组件在层次化中应该表现成子类。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

Step 2: Build a static version in React(在React中建立一个静态版本号)

如今我们的组件已经层次化了。是时间去实现我们的应用了。最简单的方式是建立一个UI是依靠你的数据模型渲染的可是并没有交互的版本号。最好要拆解这些步骤。由于建立一个静态的版本号须要在没有想法的情况下写非常多的代码,加入交互就须要思考非常多而且写非常少的代码。

以下会知道为什么。

建立一个渲染你的数据模型的应用的静态版本号,你会想去建立能复用其它组建的组件,而且使用props传递数据。props是父辈向下传递数据的方式。假设你非常熟悉state的概念。不要在这个静态版本号里面用state。state不过为了交互而存在,里边的数据是一直在变的。尽然这是一个静态版本号,那就不要用state。

你能够从上往下或反向建立。也就是你能够根据应用的层级从高层向底层(比如从FilterableProductTable開始)或反向实现(从ProductRow開始)。再简单的样例中。从上至下更easy,在大型项目中,从下向上地建立和測试应用更简单。

在这一步的最后,你会用一个可复用的组件库去渲染你的数据模型。

既然这是一个应用的静态版本号,那么组件就仅仅会拥有render()方法。在组件层顶端的组件就会把数据模型作为prop。

假设你改动底层的数据模型。然后再次调用ReactDOM.render(),UI会被更新。

非常easy就能够看到你的UI如何更新,哪里做了改变,之后就没有什么复杂的东西了。React是依靠单向数据流使得全部事情模块化和高速。

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

Step 3: Identify the minimal (but complete) representation of UI state(找到最小的UI state的代表)

为了实现你的UI的交互性。你须要去触发你的底层数据模型的改变。

React通过运用state使这变得非常easy。

为了正确的创建你的应用。第一步应该考虑的是你的应用须要的最小单位的可变state。

关键技巧就是:不要反复。找出你的应用须要的state绝对最小代表,然后去推算你须要的全部其它东西。

比如:假设你在建一个TODO列表。那就只在这TODO items周围保存一个数组;不要为了数组长度去保存一个state,假设须要,就去取数组长度。

想一下我们应用中的全部数据片段。我们有:

1.最原始的产品列表

2.用户输入的搜索文本

3.checkbox的值

4.被筛选的产品

我们挨个看一下,找出哪一个是state。对于每个数据片段提三个问题:

1.这是从父辈传过来的props吗?

假设是。则它不是state。

2.它实时在改变吗?假设不是,则不是state。

3.你能在组件中通过其它的state或者props得出它来吗?假设能,则不是state。

最原始的产品清单是被当做props传递的,它不是state。搜索文本和checkbox看起来像是state,由于它们实时在变并且不能通过其他数据得出。

最后,被筛选的产品列表不是state,由于它可以通过搜索文本,checkbox和原始产品清单得出。

所以,我们的state就是:搜索文本和checkbox的值。

Step 4: Identify where your state should live(确认你的state应该放在哪)

好的。这样我们就确认了应用中state的最小单位(set)。下一步 ,我们须要确认哪一个组件改变或者拥有这个state。

请记住,react在我们的组件层次中是单向数据流动的。

可能不会一下就看明确哪个组件拥有哪个state。这一点对新手来说常常是最有挑战性的部分,所以根据下面步骤能够弄明确:

对于你的应用中每个state:

-确认每个依靠这个state去渲染sth的组件

-找到一个组件共同拥有者(在组件层次中,位于全部组件上层的单独组件也须要这个state)

-无论是组件共同拥有者还是组件层中的上层组件都应该拥有这个state

-假设你找不到一个拥有这个state的组件,那就创建一个功能仅仅是保存这个state的组件,把它放到组件共同拥有者的上方。

我们在当前的应用中试一下上边的策略。

-ProductTable须要根据state去筛选产品列表。SearchBar展示搜索文本和checkbox状态也须要state。

-组件共同拥有者是FilterableProductTable.

-把filter text和checked value放在FilterableProductTable里边从概念上来说是行的通的。

Cool,我们就这样愉快地决定了把state放在FilterableProductTable里边。首先,在FilterableProductTable里加入一个getInitialState()方法。返回值是{filterText: ‘‘, inStockOnly: false},反映了应用的初始state。

然后,把filterText和inStockOnly当做prop传入ProductTable和SearchBar中。

最后,用这些props过滤ProductTable中的rows,设置SearchBar中的

表单字段的值。

如今你就能看到你的应用是怎样执行的:将filterText设为“ball”然后刷新应用。你就能看到数据表正确更新了。

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

STEP 5:Add inverse data flow(加入反向数据流)

到这里,我们建立了一个可以依据state和prop在模块层间的流动去正确渲染的应用。

如今是时候去支持数据的还有一种方式流动:在深层次中的表单组件须要在FilterableProductTable里去更新state。React中的这样的数据流动很明白清晰,所以可以很easy得理解你的应用是如何工作的。可是这须要比通常的双向数据绑定写很多其它的代码。React提供了额外的一个叫做ReactLink的工具让这样的模式变得像双向数据绑定一样的舒服,这样的目的就是,让全部的事情变得更明白。

假设你尝试着去在近期版本号例子里去写代码或检查checkbox,你会看到React忽略了你的输入。这是有意为之的,由于我们设置了input的prop一直是和FilterableProductTable传过来的state同样的。 我们来想一下我们想要什么样的事情发生。我们想确保不管用户什么时候改变表单。我们就去更新state以便反映出用户的输入。

既然组件应该只更新他们自己的state,FilterableProductTable会传递给SearchBar回调函数,在state须要更新的时候就会调用。

我们能够在input上使用onchange事件去通知调用callback。

被FilterableProductTable传递过来的callback会调用setState(),然后应用就会被更新了。

虽然这听起来非常复杂,可是真的仅仅是须要几行代码。而且你的应用中数据的流动过程会非常的清晰。And that‘s it。

但愿这篇文章可以帮助你弄清晰怎么用React去建立组件和应用。虽然这要比平时多写一些代码,可是请记住代码的可读性的重要性要远远超过代码的书写,阅读这些模块化和很清晰的代码是很easy的。当你開始建立大型的组件库。你就会感谢这样的明白性和模块化,随着代码的复用。你的代码量就会開始降低了。

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

------------------------------------------------我是分隔符--------------------------------------------------

原文结束。

本文主要是根据一个产品标的样例来介绍React的单向数据流的思想,从一開始如何分析站点模型的结构,如何划分组件。组件中的数据。数据之间的关系,根据这些关系,有固定的方法来帮助我们决定哪一些数据属于state,哪一些是prop,确定了state之后,就能够给state依存的组件加入监听方法。监听input输入的变化。从而实时更新数据模型。又一次渲染界面。

时间: 2024-11-08 15:54:40

Thinking in React(翻译)的相关文章

React翻译官网文档之JSX

什么是JSX? 看下面的代码它被称为JSX,它既不是字符串也不是HTML,而是一种facebook公司对javascript语法的拓展.虽然写法很奇怪最终仍会会被编译为javascript代码 const element = <h1>Hello, world!</h1>; 你可以在JSX中嵌入任何javascript表达式,看下面的例子. function formatName(user) { return user.firstName + ' ' + user.lastName;

React学习系列

React学习系列 系列学习react 翻译地址 https://scotch.io/tutorials/learning-react-getting-started-and-concepts 我是初学者,英语也不是很好,不过一直强迫自己看英文文档. 这是理解翻译,翻译的不好,请见谅!()中的是我翻译过程中理解,参考下,有什么说的不对的欢迎指点下! 第一节:如何开始react和了解react的概念 1.React是什么 react是Facebook 开发出来用于促进UI交互,创建带有状态的,可复

React学习系列一

系列学习react 翻译地址 https://scotch.io/tutorials/learning-react-getting-started-and-concepts 我是初学者,英语也不是很好,不过一直强迫自己看英文文档. 这是理解翻译,翻译的不好,请见谅!()中的是我翻译过程中理解,参考下,有什么说的不对的欢迎指点下! 第一节:如何开始react和了解react的概念 1.React是什么 react是Facebook 开发出来用于促进UI交互,创建带有状态的,可复用的UI组建的UI库

【翻译】光速React – Vixlet

翻译原文链接:https://blog.vixlet.com/react-at-light-speed-78cd172a6411 个人翻译小站链接:http://www.zcfy.cc/article/react-at-light-speed-vixlet-2920.html 在过去的几年里, 我们Vixlet (http://www.vixlet.com) 的web团队,着手了一项激动人心的项目,将我们的整个web应用迁往React+Redux的建设.对于我们整个团队来说 这是一个不断增长的机

不止于小程序 APICloud推出react native纯翻译模式的UI引擎

[快速阅读,文章概要]APICloud作为国内领先的API生态平台服务商,提供的APICloud移动云平台能够一站式的满足app开发.版本更新.API云服务集成.数据运营等移动全生命周期的管理.使开发者能够在2到3个月内"快速"开发一款app. 在<AI时代的移动技术革新>大会中,APICloud团队发布了小程序的技术规范兼容产品,一种类似于React Native技术的纯翻译模式的app UI引擎,能够让app开发者利用简单的网页模式技术开发真正原生体验的app. 同时A

react项目开发中出现浏览器翻译功能造成的bug

最近使用react开发一个项目时,测试提出一个bug,说是在某些浏览器上面本来是序号‘’5‘’变成了大写的‘’五‘’ 自己却一直无法重现,最后发现是测试人员浏览器打开了翻译功能 解决方案: 解决思路是将index.ejs的html lang='en'改为lang='zh',这样设置网页就不会自动翻译了,就是翻译了也只是中文翻译成中文,不会再出现大写五了. 后续: 不过如果自己还将网页翻译为中文,列表页存在一个问题就是,翻页的时候列表数据刷新不出来 右侧数据有的没有被渲染成功 原文地址:https

翻译React官网文档—— 入门创建一个Hello World

React的初衷是一个用于构建View层(界面)的javascript类库.后来规模越来越多又有了ReactNative. 不赘述如何搭建react了,由于是学习我们直接用CodePen(可能需要FQ,可以自行下载免费的Lantern),Codepen中我们可以自由的修改代码 通常学习任何语言都要写一个Hello World.Tack is cheep 直接上代码 Go! ReactDOM.render( <h1>Hello, world!</h1>, document.getEl

react与jQuery对比,有空的时候再翻译一下

参考资料:http://reactfordesigners.com/labs/reactjs-introduction-for-people-who-know-just-enough-jquery-to-get-by/ Target Audience: People Who Know Just Enough jQuery to Get by Before I begin, I'd like to clarify who my target audience is. Zed Shaw, the a

如何写好react组件

react 组件方面: 总结 React 组件的三种写法 及最佳实践 [涨经验] React组件编写思路(一) 使用react-router实现单页面应用时设置页面间过渡的两种方式 [翻译]基于 Create React App路由4.0的异步组件加载(Code Splitting) React进阶--使用高阶组件(Higher-order Components)优化你的代码 Higher-order Components 高阶组件 redux方面: Redux-saga 中文文档 https: