Thinking in React
反思中的思考(Thinking in React)
在我们看来,React 是用 JavaScript 构建大型、快速 Web 应用程序的主要方式。它在 Facebook 和 Instagram 上对我们的扩展非常好。
React 的许多重要组成部分之一就是如何让您在构建应用程序时考虑应用程序。在本文中,我们将引导您使用 React 构建可搜索产品数据表的思考过程。
从模拟开始
想象一下,我们已经有了一个 JSON API 和来自我们设计师的模拟器。模拟看起来像这样:
我们的 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"}
];
第1步:将 UI 分解为组件层次结构
你要做的第一件事是在模拟中的每个组件(和子组件)周围绘制框,并给它们所有名称。如果您正在与设计师合作,他们可能已经完成了这项工作,请与他们谈谈!他们的 Photoshop 图层名称可能最终会成为您的React组件的名称!
但你怎么知道自己的组件应该是什么?只需使用相同的技巧来决定是否应创建新的函数或对象。一种这样的技术是单一责任原则
,也就是说,一个组件理想地只应该做一件事。如果它最终增长,它应该被分解成更小的子组件。
由于您经常向用户显示 JSON 数据模型,因此您会发现,如果您的模型构建正确,那么您的 UI(以及您的组件结构)将很好地映射。这是因为UI和数据模型倾向于遵循相同的信息架构
,这意味着将 UI 分解为组件的工作通常是微不足道的。只需将其分解为完全代表您的数据模型的一部分的组件。
您会在这里看到我们的简单应用中有五个组件。我们已将每个组件代表的数据以斜体表示。
FilterableProductTable
(橙色):
包含整个示例
如果你看一下ProductTable
,你会发现表头(包含“名称”和“价格”标签)不是它自己的组件。这是一个偏好的问题,有两种方法都有争论。对于这个例子,我们将它作为其一部分,ProductTable
因为它是呈现数据收集的
一部分,这是ProductTable
责任。然而,如果这个头部变得复杂(即,如果我们要为分类添加可供性),将它作为它自己的ProductTableHeader
组件将是有意义的。
现在我们已经确定了模拟中的组件,让我们将它们安排到一个层次结构中。这很容易。出现在模拟中另一个组件中的组件应该在层次结构中显示为一个子组件:
FilterableProductTable
- `SearchBar`
- `ProductTable`
- `ProductCategoryRow`
- `ProductRow`
第2步:在 React 中构建静态版本
见笔思维阵营:第2步
上CodePen
。
现在您已经拥有组件层次结构,现在可以实施您的应用了。最简单的方法是构建一个接收数据模型并呈现 UI 但不具有交互性的版本。最好解耦这些过程,因为构建一个静态版本需要大量的输入和思考,而添加交互性需要大量的思考,而不是大量的输入。我们会看到为什么。
要构建呈现数据模型的应用程序的静态版本,您需要构建可重用其他组件并使用道具
传递数据的组件。道具
是一种将数据从父母传递给孩子的方式。如果您熟悉状态
的概念,请不要使用状态
来构建此静态版本。状态
仅用于交互性,即随时间变化的数据。由于这是应用程序的静态版本,因此您不需要它。
您可以构建自上而下或自下而上。也就是说,您可以从构建层次结构中较高级的组件(即以开始FilterableProductTable
)或以较低的组件(ProductRow
)开始构建组件。在更简单的例子中,自上而下通常会更容易,而在大型项目中,自下而上和编写测试时更容易。
在这一步结束时,您将拥有一个可重用组件的库,用于呈现您的数据模型。组件只会有render()
方法,因为这是您的应用程序的静态版本。层次结构顶部的组件(FilterableProductTable
)将把您的数据模型作为一个道具。如果您对基础数据模型进行了更改并ReactDOM.render()
再次调用,则UI将会更新。很容易看到你的UI如何更新以及在哪里进行更改,因为没有任何复杂的事情发生。React 的单向数据流
(也称为单向绑定
)可以保持一切模块化和快速。
如果您需要帮助执行此步骤,请参阅 React 文档
。
一个简短的插曲:道具与陈述(Props vs State)
React 中有两种“模型”数据:道具和状态。了解两者之间的区别很重要; 如果您不确定区别是什么,请浏览官方的 React 文档。
第3步:确定 UI 状态的最小(但完整)表示形式
为了使您的 UI 交互,您需要能够触发对基础数据模型的更改。React 使状态
变得简单。
要正确构建应用程序,首先需要考虑应用程序需要的最小可变状态集。这里的关键是 DRY:不要重复自己
。找出您的应用程序需要的状态的绝对最小表示形式,并计算您需要的其他所有内容。例如,如果您正在创建一个 TODO 列表,只需保留一组 TODO 项目; 不要为计数保留一个单独的状态变量。相反,当您要渲染 TODO 计数时,只需简单计算 TODO 项目数组的长度。
考虑我们示例应用程序中的所有数据。我们有:
- 产品的原始列表
我们来看看每一个,并确定哪一个是状态。只需询问关于每一条数据的三个问题:
- 它是通过道具从父数据传入吗?如果是这样,它可能不是陈述。
原来的产品清单是作为道具传入的,所以这不是状态。搜索文本和复选框似乎是状态的,因为它们随着时间而改变并且不能从任何东西计算。最后,过滤后的产品列表不是状态,因为它可以通过将原始产品列表与复选框的搜索文本和值组合来计算。
最后,我们的状态是:
- 用户输入的搜索文本
第4步:确定你的国家应该居住在哪里
请参阅笔反思:在CodePen上的第4 步
。
好的,我们已经确定了最少的应用程序状态集。接下来,我们需要确定哪个组件变异或拥有
这个状态。
请记住:React 是关于组件层次结构下的单向数据流。它可能不会立即清楚哪个组件应该拥有什么状态。对于新手来说,这通常是最具挑战性的部分,
所以请按照以下步骤来弄清楚:
对于你的应用程序中的每一个状态:
- 确定每个基于该状态呈现某些内容的组件。
让我们来看看我们的应用程序的这个策略:
ProductTable
需要根据状态过滤产品列表,同时SearchBar
需要显示搜索文本和选中状态。
所以我们决定我们的国家居住在英国FilterableProductTable
。首先,添加一个实例属性this.state = {filterText: '', inStockOnly: false}
到FilterableProductTable
's constructor
来反映你的应用程序的初始状态。然后,通过filterText
与inStockOnly
以ProductTable
和SearchBar
为道具。最后,使用这些道具过滤行中ProductTable
并设置表单域的值SearchBar
。
你可以开始看到你的应用程序将如何表现:设置filterText
到"ball"
和刷新您的应用程序。您会看到数据表已正确更新。
第5步:添加反向数据流
见笔思维阵营:第5步上CodePen
。
到目前为止,我们已经构建了一个应用程序,可以根据道具和状态正确地呈现在层次结构中。现在是时候以另一种方式支持数据流:深层次中的表单组件需要更新状态FilterableProductTable
。
React 使这个数据流清晰易懂,以便理解你的程序如何工作,但它确实需要比传统的双向数据绑定更多的输入。
如果您尝试在当前版本的示例中键入或选中该框,您会看到 React 忽略了您的输入。这是故意的,因为我们已经设置value
的道具input
总是等于state
从传递FilterableProductTable
。
让我们思考我们想要发生的事情。我们希望确保每当用户更改表单时,我们都会更新状态以反映用户输入。由于组件应该只更新自己的状态,FilterableProductTable
因此SearchBar
只要状态应该更新,就会传递回调。我们可以使用onChange
输入中的事件来通知它。通过的回调FilterableProductTable
将会调用setState()
,并且应用程序将被更新。
虽然这听起来很复杂,但它只是几行代码。这真的很明确你的数据如何在整个应用程序中流动。
就是这样
希望这可以让你了解如何使用 React 构建组件和应用程序。虽然它可能比您习惯的输入要多一些,但请记住,代码的读取远远大于其编写的代码,并且读取此模块化的显式代码非常容易。当你开始构建大型组件库时,你会明白这种明确性和模块性,并且随着代码的重用,你的代码行将开始缩小。:)