mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
612 lines
28 KiB
HTML
612 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<!--[if IE]><![endif]-->
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||
<title>React 编程思想 | React</title>
|
||
<meta name="viewport" content="width=device-width">
|
||
<meta property="og:title" content="React 编程思想 | React">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://facebook.github.io/react/docs/thinking-in-react.zh-CN.html">
|
||
<meta property="og:image" content="https://facebook.github.io/react/img/logo_og.png">
|
||
<meta property="og:description" content="A JavaScript library for building user interfaces">
|
||
<meta property="fb:app_id" content="623268441017527">
|
||
|
||
<link rel="shortcut icon" href="/react/favicon.ico">
|
||
<link rel="alternate" type="application/rss+xml" title="React" href="https://facebook.github.io/react/feed.xml">
|
||
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css" />
|
||
<link rel="stylesheet" href="/react/css/syntax.css">
|
||
<link rel="stylesheet" href="/react/css/codemirror.css">
|
||
<link rel="stylesheet" href="/react/css/react.css">
|
||
|
||
<script src="//use.typekit.net/vqa1hcx.js"></script>
|
||
<script>try{Typekit.load();}catch(e){}</script>
|
||
|
||
<!--[if lte IE 8]>
|
||
<script src="/react/js/html5shiv.min.js"></script>
|
||
<script src="/react/js/es5-shim.min.js"></script>
|
||
<script src="/react/js/es5-sham.min.js"></script>
|
||
<![endif]-->
|
||
<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script>
|
||
<script src="/react/js/codemirror.js"></script>
|
||
<script src="/react/js/javascript.js"></script>
|
||
<script src="/react/js/react.js"></script>
|
||
<script src="/react/js/react-dom.js"></script>
|
||
<script src="/react/js/babel-browser.min.js"></script>
|
||
<script src="/react/js/live_editor.js"></script>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="container">
|
||
|
||
<div class="nav-main">
|
||
<div class="wrap">
|
||
<a class="nav-home" href="/react/index.html">
|
||
<img class="nav-logo" src="/react/img/logo.svg" width="36" height="36">
|
||
React
|
||
</a>
|
||
<ul class="nav-site nav-site-internal">
|
||
<li><a href="/react/docs/getting-started.html" class="active">Docs</a></li>
|
||
<li><a href="/react/support.html">Support</a></li>
|
||
<li><a href="/react/downloads.html">Download</a></li>
|
||
<li><a href="/react/blog/">Blog</a></li>
|
||
<li>
|
||
<input id="algolia-doc-search" type="text" placeholder="Search docs..." />
|
||
</li>
|
||
</ul>
|
||
|
||
<ul class="nav-site nav-site-external">
|
||
<li><a href="https://github.com/facebook/react">GitHub</a></li>
|
||
<li><a href="https://facebook.github.io/react-native/">React Native</a></li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<section class="content wrap documentationContent">
|
||
<div class="nav-docs">
|
||
<!-- Docs Nav -->
|
||
|
||
<div class="nav-docs-section">
|
||
<h3>Quick Start</h3>
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="/react/docs/getting-started.html">Getting Started</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/tutorial.html">Tutorial</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/thinking-in-react.html">Thinking in React</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="nav-docs-section">
|
||
<h3>Community Resources</h3>
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="/react/docs/conferences.html">Conferences</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/videos.html">Videos</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://github.com/facebook/react/wiki/Complementary-Tools" class="external">Complementary Tools</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://github.com/facebook/react/wiki/Examples" class="external">Examples</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="nav-docs-section">
|
||
<h3>Guides</h3>
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="/react/docs/why-react.html">Why React?</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/displaying-data.html">Displaying Data</a>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="/react/docs/jsx-in-depth.html">JSX in Depth</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/jsx-spread.html">JSX Spread Attributes</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/jsx-gotchas.html">JSX Gotchas</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/interactivity-and-dynamic-uis.html">Interactivity and Dynamic UIs</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/multiple-components.html">Multiple Components</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/reusable-components.html">Reusable Components</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/transferring-props.html">Transferring Props</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/forms.html">Forms</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/working-with-the-browser.html">Working With the Browser</a>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="/react/docs/more-about-refs.html">Refs to Components</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/tooling-integration.html">Tooling Integration</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/addons.html">Add-Ons</a>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="/react/docs/animation.html">Animation</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/two-way-binding-helpers.html">Two-Way Binding Helpers</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/test-utils.html">Test Utilities</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/clone-with-props.html">Cloning Elements</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/create-fragment.html">Keyed Fragments</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/update.html">Immutability Helpers</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/pure-render-mixin.html">PureRenderMixin</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/perf.html">Performance Tools</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/shallow-compare.html">Shallow Compare</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/advanced-performance.html">Advanced Performance</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/context.html">Context</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="nav-docs-section">
|
||
<h3>Reference</h3>
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="/react/docs/top-level-api.html">Top-Level API</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/component-api.html">Component API</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/component-specs.html">Component Specs and Lifecycle</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/tags-and-attributes.html">Supported Tags and Attributes</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/events.html">Event System</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/dom-differences.html">DOM Differences</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/special-non-dom-attributes.html">Special Non-DOM Attributes</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/reconciliation.html">Reconciliation</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/webcomponents.html">Web Components</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/docs/glossary.html">React (Virtual) DOM Terminology</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="nav-docs-section">
|
||
<h3>Flux</h3>
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="https://facebook.github.io/flux/docs/overview.html" class="external">Flux Overview</a>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://facebook.github.io/flux/docs/todo-list.html" class="external">Flux TodoMVC Tutorial</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
|
||
<!-- Tips Nav -->
|
||
|
||
<div class="nav-docs-section">
|
||
<h3>Tips</h3>
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="/react/tips/introduction.html">Introduction</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/inline-styles.html">Inline Styles</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/if-else-in-JSX.html">If-Else in JSX</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/self-closing-tag.html">Self-Closing Tag</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/maximum-number-of-jsx-root-nodes.html">Maximum Number of JSX Root Nodes</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/style-props-value-px.html">Shorthand for Specifying Pixel Values in style props</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/children-props-type.html">Type of the Children props</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/controlled-input-null-value.html">Value of null for Controlled Input</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/componentWillReceiveProps-not-triggered-after-mounting.html">componentWillReceiveProps Not Triggered After Mounting</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/props-in-getInitialState-as-anti-pattern.html">Props in getInitialState Is an Anti-Pattern</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/dom-event-listeners.html">DOM Event Listeners in a Component</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/initial-ajax.html">Load Initial Data via AJAX</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/false-in-jsx.html">False in JSX</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/communicate-between-components.html">Communicate Between Components</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/expose-component-functions.html">Expose Component Functions</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/children-undefined.html">this.props.children undefined</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/use-react-with-other-libraries.html">Use React with Other Libraries</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/react/tips/dangerously-set-inner-html.html">Dangerously Set innerHTML</a>
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
<div class="inner-content">
|
||
<h1>
|
||
React 编程思想
|
||
<a class="edit-page-link" href="https://github.com/facebook/react/tree/master/docs/docs/thinking-in-react.zh-CN.md" target="_blank">Edit on GitHub</a>
|
||
</h1>
|
||
<div class="subHeader"></div>
|
||
|
||
<p>by Pete Hunt</p>
|
||
|
||
<p>在我看来,React 是构建大型,快速 Web app 的首选方式。它已经在 Facebook 和 Instagram 被我们有了广泛的应用。</p>
|
||
|
||
<p>React 许多优秀的部分之一,是它使得你在构建 app 的过程中不断思考。在本文里,我将带你经历一次使用 React 构建可搜索的商品数据表的思考过程。</p>
|
||
<h2><a class="anchor" name="mock"></a>从模型(mock)开始 <a class="hash-link" href="#mock">#</a></h2>
|
||
<p>想象我们已经有个一个 JSON API 和一个来自设计师的模型。我们的设计师显然做得不够好,因为模型看起来像这样:</p>
|
||
|
||
<p><img src="/react/img/blog/thinking-in-react-mock.png" alt="Mockup"></p>
|
||
|
||
<p>我们的 JSON API 返回一些看起来像这样的数据:</p>
|
||
<div class="highlight"><pre><code class="language-text" data-lang="text">[
|
||
{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"}
|
||
];
|
||
</code></pre></div><h2><a class="anchor" name="ui"></a>第一步:把UI拆分为一个组件的层级 <a class="hash-link" href="#ui">#</a></h2>
|
||
<p>首先你想要做的,是在模型里的每一个组件周围绘制边框,并给它们命名。如果你和设计师一起工作,他们应该已经完成这步了,所以去和他们谈谈!他们的 Photoshop 图层名也许最终会成为你的 React 组件名。</p>
|
||
|
||
<p>但是你如何知道什么东西应该是独立的组件?只需在你创建一个函数或者对象时,根据是否使用过相同技术来做决定。一种这样的技术是<a href="https://en.wikipedia.org/wiki/Single_responsibility_principle">单一功能原则(single responsibility principle)</a>,也就是一个组件在理想情况下只做一件事情。如果它最终增长了,它就应该被分解为更小的组件。</p>
|
||
|
||
<p>既然你频繁显示一个 JSON 的数据模型给用户,你会发现,如果你的模型构建正确,你的 UI(因此也有你的组件结构)就将映射良好。那是因为 UI 和数据模型趋向附着于相同的 <em>信息架构</em>,这意味着,把你的 UI 分离为组件的工作通常是琐碎的,只需把 UI 拆分成能准确对应数据模型的每块组件。</p>
|
||
|
||
<p><img src="/react/img/blog/thinking-in-react-components.png" alt="Component diagram"></p>
|
||
|
||
<p>在这里你会看到,在我们的简单 APP 里有五个组件。我用斜体表示每个组件的数据。</p>
|
||
|
||
<ol>
|
||
<li><strong><code>FilterableProductTable</code> (橙色):</strong> 包含示例的整体</li>
|
||
<li><strong><code>SearchBar</code> (蓝色):</strong> 接收所有 <em>用户输入</em></li>
|
||
<li><strong><code>ProductTable</code> (绿色):</strong> 基于 <em>用户输入</em> 显示和过滤 <em>数据集合(data collection)</em></li>
|
||
<li><strong><code>ProductCategoryRow</code> (蓝绿色):</strong> 为每个 <em>分类</em> 显示一个列表头</li>
|
||
<li><strong><code>ProductRow</code> (红色):</strong> 为每个 <em>商品</em> 显示一行</li>
|
||
</ol>
|
||
|
||
<p>如果你看着 <code>ProductTable</code>,你会看到表头(包含了 "Name" 和 "Price" 标签) 不是独立的组件。这是一个个人喜好问题,并且无论采用哪种方式都有争论。对于这个例子,我把它留做 <code>ProductTable</code> 的一部分,因为它是 <em>data collection</em>渲染的一部分,而 <em>data collection</em> 渲染是 <code>ProductTable</code> 的职责。然而,当列表头增长到复杂的时候(例如 如果我们添加排序功能),那么使它成为独立的 <code>ProductTableHeader</code> 组件无疑是有意义的。</p>
|
||
|
||
<p>既然现在我们已经识别出了我们模型中的组件,让我们把他们安排到一个层级中。这很容易。在模型中,出现在一个组件里面的另一组件 ,应该在层级中表现为一种子级关系:</p>
|
||
|
||
<ul>
|
||
<li><code>FilterableProductTable</code>
|
||
|
||
<ul>
|
||
<li><code>SearchBar</code></li>
|
||
<li><code>ProductTable</code>
|
||
|
||
<ul>
|
||
<li><code>ProductCategoryRow</code></li>
|
||
<li><code>ProductRow</code></li>
|
||
</ul></li>
|
||
</ul></li>
|
||
</ul>
|
||
<h2><a class="anchor" name="react"></a>第二步:用React创建一个静态版本 <a class="hash-link" href="#react">#</a></h2>
|
||
<iframe width="100%" height="600" src="https://jsfiddle.net/reactjs/yun1vgqb/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||
|
||
<p>既然你已经有了你的组件层级,是时候实现你的app了。简单的方式是构建一个版本,它取走你的数据模型并渲染UI,除了没有互动性。这是将过程解耦的最好办法,因为构建一个静态版本需要不假思索地写很多代码,而添加互动性需要很多思考但不需要太多代码。之后我们将会看到原因。</p>
|
||
|
||
<p>要构建一个静态版本 app 来渲染你的数据模型,你将会想到构建一个重用其它组件并利用 <em>props</em> 传递数据的组件。<em>props</em> 是一种从父级传递数据到子级的方式。如果你对 <em>state</em> 的观念很熟悉,<strong>绝不要用state</strong> 来构建这个静态版本。State 仅仅是为互动性,也就是随时间变化的数据所预留的。由于这是一个静态版本,你还不需要用到它。</p>
|
||
|
||
<p>你可以自顶向下或自底向上的构建。也就是说,你可以既从较高的层级(比如从 <code>FilterableProductTable</code> 开始)也可以从较低的层级(<code>ProductRow</code>)开始构建组件。在较简单的例子里,通常自顶向下要容易一些,然而在更大的项目上,自底向上地构建更容易,并且更方便伴随着构建写测试。</p>
|
||
|
||
<p>在这一步的最后,你会获得一个渲染数据模型的可重用组件库。这些组件只有 <code>render()</code> 方法,因为这是一个静态版本。在层级顶端的组件 (<code>FilterableProductTable</code>) 将会接受你的数据模型,并将其作为一个prop。如果你改变了底层数据模型,并且再次调用 <code>React.render()</code> ,UI 将会更新。你可以很容易地看到 UI 是如何更新的,以及哪里变动了,因为这没什么复杂的。React的 <strong>单向数据流</strong> (也被称为 <em>单向绑定</em>)使一切保持了模块化和快速。</p>
|
||
|
||
<p>如果你在执行这步时需要帮助,请参阅 <a href="/react/docs/">React 文档</a>。</p>
|
||
<h3><a class="anchor" name="-props-vs-state"></a>小插曲: props vs state <a class="hash-link" href="#-props-vs-state">#</a></h3>
|
||
<p>在React里有两种数据 "模型": props 和 state。明白这二者之间的区别是很重要的;如果你不是很确定它们之间的区别,请概览<a href="/react/docs/interactivity-and-dynamic-uis-zh-CN.html">React官方文档</a></p>
|
||
<h2><a class="anchor" name="-ui-state-"></a>第三步:确定最小(但完备)的 UI state 表达 <a class="hash-link" href="#-ui-state-">#</a></h2>
|
||
<p>要让你的 UI 互动,你需要做到触发底层数据模型发生变化。React用 <strong>state</strong> 来让此变得容易。</p>
|
||
|
||
<p>要正确的构建你的 app,你首先需要思考你的 app 需要的可变 state 的最小组。这里的关键是 DRY 原则:<em>Don't Repeat Yourself(不要重复自己)</em>。想出哪些是你的应用需要的绝对最小 state 表达,并按需计算其他任何数据。例如,如果你要构建一个 TODO list,只要保持一个 TODO 项的数组;不要为了计数保持一个单独的 state 变量。当你想渲染 TODO 的计数时,简单的采用 TODO 项目的数组长度作为替代。</p>
|
||
|
||
<p>考虑我们示例应用中的数据所有块,包括:</p>
|
||
|
||
<ul>
|
||
<li>原始的商品列表</li>
|
||
<li>用户输入的搜索文本</li>
|
||
<li>复选框的值</li>
|
||
<li>商品的过滤列表</li>
|
||
</ul>
|
||
|
||
<p>让我们逐个检查出哪一个是state,只需要简单地问以下三个问题:</p>
|
||
|
||
<ol>
|
||
<li>它是通过props从父级传递来的吗?如果是,它可能不是 state。</li>
|
||
<li>它随时间变化吗?如果不是,它可能不是 state。</li>
|
||
<li>你能基于其他任何组件里的 state 或者 props 计算出它吗?如果是,它可能不是state.</li>
|
||
</ol>
|
||
|
||
<p>原始的商品列表以 props 传入,所以它不是 state。搜索文本和复选框看起来是 state,因为他们随时间变化并且不能从任何东西计算出。最后,过滤出的商品列表不是 state,因为它可以通过原始列表与搜索文本及复选框的值组合计算得出。</p>
|
||
|
||
<p>所以最后,我们的 state 是:</p>
|
||
|
||
<ul>
|
||
<li>用户输入的搜索文本</li>
|
||
<li>checkbox 的值</li>
|
||
</ul>
|
||
<h2><a class="anchor" name="-state-"></a>第四步:确定你的 state 应该存在于哪里 <a class="hash-link" href="#-state-">#</a></h2>
|
||
<iframe width="100%" height="600" src="https://jsfiddle.net/reactjs/zafjbw1e/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||
|
||
<p>OK,我们已经确定好应用的最小 state 集合是什么。接下来,我们需要确定哪个组件可以改变,或者 <em>拥有</em> 这个state.</p>
|
||
|
||
<p>记住:React 总是在组件层级中单向数据流动的。可能不能立刻明白哪些组件应该拥有哪些 state。 <strong>这对于新手在理解上经常是最具挑战的一部分,</strong> 所以跟着这几步来弄明白它:</p>
|
||
|
||
<p>对于你的应用里每一个数据块:</p>
|
||
|
||
<ul>
|
||
<li>确定哪些组件要基于 state 来渲染内容。</li>
|
||
<li>找到一个共同的拥有者组件(在所有需要这个state组件的层次之上,找出共有的单一组件)。</li>
|
||
<li>要么是共同拥有者,要么是其他在层级里更高级的组件应该拥有这个state。</li>
|
||
<li>如果你不能找到一个组件让其可以有意义地拥有这个 state,可以简单地创建一个新的组件 hold 住这个state,并把它添加到比共同拥有者组件更高的层级上。</li>
|
||
</ul>
|
||
|
||
<p>让我们使用这个策略浏览一遍我们的应用:</p>
|
||
|
||
<ul>
|
||
<li><code>ProductTable</code> 需要基于 state 过滤产品列表,<code>SearchBar</code> 需要显示搜索文本和选择状态。</li>
|
||
<li>共同拥有者组件是 <code>FilterableProductTable</code>。</li>
|
||
<li>对于过滤文本和选择框值存在于 <code>FilterableProductTable</code>,从概念上讲是有意义的。</li>
|
||
</ul>
|
||
|
||
<p>酷,我们已经决定了我们的 state 存在于 <code>FilterableProductTable</code>。首先,添加一个 <code>getInitialState()</code> 方法到 <code>FilterableProductTable</code>,返回 <code>{filterText: '', inStockOnly: false}</code> 来反映应用的初始状态。然后,传递<code>filterText</code> 和 <code>inStockOnly</code> 给 <code>ProductTable</code> 和 <code>SearchBar</code> 作为 prop。最后,使用这些 prop 来过滤 <code>ProductTable</code> 中的行和设置 <code>SearchBar</code> 的表单项的值。</p>
|
||
|
||
<p>你可以开始看看你的应用将有怎样的行为了: 设置 <code>filterText</code> 为 <code>"ball"</code> 并刷新你的 app。你将可以看到数据表被正确地更新。</p>
|
||
<h2><a class="anchor" name=""></a>第五步:添加反向数据流 <a class="hash-link" href="#">#</a></h2>
|
||
<iframe width="100%" height="600" src="https://jsfiddle.net/reactjs/n47gckhr/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
|
||
|
||
<p>到目前为止,我们已经构建了一个应用, 它以 props 和 state 沿着层级向下流动的功能正确渲染。现在是时候支持另一种数据流动了:在层级深处的表单组件需要更新 <code>FilterableProductTable</code> 里的 state。</p>
|
||
|
||
<p>React 让数据显式流动,使你理解应用如何工作变得简单,但是相对于传统的双向数据绑定,确实需要多打一些字。React 提供了一个叫做 <code>ReactLink</code> 的插件来使这种模式和双向数据绑定一样方便,但是考虑到这篇文章的目的,我们将会保持所有东西都直截了当。</p>
|
||
|
||
<p>如果你尝试在当前版本的示例中输入或者选中复选框,你会发现 React 忽略了你的输入。这是有意的,因为我们已经设置了 <code>input</code> 的 <code>value</code> prop 值总是与 <code>FilterableProductTable</code> 传递过来的 <code>state</code> 一致。</p>
|
||
|
||
<p>让我们思考下希望发生什么。我们想确保每当用户改变表单,就通过更新 state 来反映用户的输入。由于组件应该只更新自己拥有的 state , <code>FilterableProductTable</code> 将会传递一个回调函数给 <code>SearchBar</code> ,每当 state 应被更新时回调函数就会被调用。我们可以使用 input 的 <code>onChange</code> 事件来接受它的通知。 <code>FilterableProductTable</code> 传递的回调函数将会调用 <code>setState()</code> ,然后应用将会被更新。</p>
|
||
|
||
<p>虽然这听起来复杂,但是实际上只是数行代码。并且这明确显示出了数据在应用中从始至终是如何流动的。</p>
|
||
<h2><a class="anchor" name=""></a>好了,就是这样 <a class="hash-link" href="#">#</a></h2>
|
||
<p>希望这给了你一个怎样思考用React构建组件和应用的概念。虽然可能比你过往的习惯要多敲一点代码,但记住,读代码的时间远比写代码的时间多,并且阅读这种模块化的、显式的代码是极为容易的。当你开始构建大型组件库时,你会非常感激这种清晰性和模块化,并且随着代码的重用,你的代码行数将会开始缩减。:)</p>
|
||
|
||
|
||
<div class="docs-prevnext">
|
||
|
||
<a class="docs-prev" href="/react/docs/tutorial-zh-CN.html">← Prev</a>
|
||
|
||
|
||
<a class="docs-next" href="/react/docs/conferences-zh-CN.html">Next →</a>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<footer class="wrap">
|
||
<div class="left">
|
||
A Facebook & Instagram collaboration.<br>
|
||
<a href="/react/acknowledgements.html">Acknowledgements</a>
|
||
</div>
|
||
<div class="right">
|
||
© 2013–2016 Facebook Inc.<br>
|
||
Documentation licensed under <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
<div id="fb-root"></div>
|
||
|
||
<script>
|
||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||
ga('create', 'UA-41298772-1', 'facebook.github.io');
|
||
ga('send', 'pageview');
|
||
|
||
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
|
||
|
||
(function(d, s, id) {
|
||
var js, fjs = d.getElementsByTagName(s)[0];
|
||
if (d.getElementById(id)) return;
|
||
js = d.createElement(s); js.id = id;
|
||
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=623268441017527";
|
||
fjs.parentNode.insertBefore(js, fjs);
|
||
}(document, 'script', 'facebook-jssdk'));
|
||
|
||
docsearch({
|
||
apiKey: '36221914cce388c46d0420343e0bb32e',
|
||
indexName: 'react',
|
||
inputSelector: '#algolia-doc-search'
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|