React中文文档之Thinking in React

    xiaoxiao2021-08-17  112

    Thinking in React - 思考React 在我们看来,React是使用js来创建大的、速度快的web应用的首选方式。它已经在Facebook和Instagram表现的非常好。 React众多伟大部分之一是:创建应用时,它是如何让你思考的。在这个文档中,我们将带领你穿越,使用React来构建搜索产品数据表的整个思维过程 Start With A Mock - 以一个模拟开始 想象一下,我们已经有一个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"} ]; Step 1: Break The UI Into A Component Hierarchy - 第一步:打破UI,成为一个组件层次结构 你将做的第一件事情是,在模拟环境下给每个组件(和子组件)周围画上方框,并给定它们名称。如果你正在和一个设计师合作,他们可能已经做了这个,因此去和他们交流!他们的PS的图层名可能就是你的React组件名称。 但是你怎么知道哪些应该是组件?当你决定创建一个新函数或对象(创建组件的2种方式:函数和对象)时,就参考下面这些相同的技巧。技巧之一是:单一职责原则,这就是,一个组件原则上制作一件事。如果它逐渐扩展了,就应该被拆分为更细的组件。 由于我们经常展示一个JSON数据模型给用户,你将发现:如果你的模型被正确地建立,你的UI(也就是组件结构)也将很好的映射。这是因为UI和数据模型倾向于支持同样的信息架构,这意味着拆分UI为组件的工作总是琐碎的。我们要做的就是:将UI拆分成准确代表部分数据模型的一个个组件。 这里你将看到,在我们简单的应用中有5个组件,我们用斜体表示代表组件的数据。 1.FilterableProductTable(橙色):包含整个示例 2.SearchBar(蓝色):接收所有的用户输入 3.ProductTable(绿色):基于用户输入,展示和过滤数据集合 4.ProductCategoryRow(青绿色):展示每个分类头 5.ProductRow(红色):展示每个产品的详情 如果你在看 'ProductTable' 组件,你将发现表头(包含 'Name' 和 'Price' 标签)不是它自己的组件。这是一个偏好问题,关于是否这样使用存在争论。针对这个例子,我们留下它作为 'ProductTable' 组件的一部分,因为它是渲染 'data' 集合的一部分,是 'ProductTable' 组件的职责(之前说过:组件单一职责原则)。然而,如果这个表头变的复杂(例如:如果我们增加了字段排序功能),制作一个 'ProductTableHeader' 组件可能更有意义。 现在,我们已经在模拟环境中确定了我们的组件,让我们将它们排列成一个层级结构: FilterableProductTable SearchBar ProductTable ProductCategoryRow ProductRow Step 2: Build A Static Version in React - 第二步:在React中构建一个静态版本 class ProductCategoryRow extends React.Component { render() { return <tr><th colSpan="2">{this.props.category}</th></tr>; } } class ProductRow extends React.Component { render() { 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> ); } } class ProductTable extends React.Component { render() { 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> ); } } class SearchBar extends React.Component { render() { return ( <form> <input type="text" placeholder="Search..." /> <p> <input type="checkbox" /> {' '} Only show products in stock </p> </form> ); } } class FilterableProductTable extends React.Component { render() { 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') ); 现在,你已经有了你的组件层级结构,是时候实现你的应用了。建立一个版本的最简单的方式是:定义数据模型和渲染UI,但是它们之间没有任何交互。最好解耦这些过程,因为构建一个静态版本需要大量的键入,而且不用思考;而添加交互则需要大量思考,并不需要很多的键入。我们会看到这是为什么。 为了构建你应用的一个静态版本,来渲染你的数据模型,你想要构建组件,这些组件重用其他组件,并通过 'props' 来传递数据。'props' 是从父传递给子数据的一种方式。如果你熟悉 'state' 概念,构建静态版本时,千万不要使用 'state'!'state' 仅用于交互,也就是,数据随着时间改变。由于这是一个静态版本,你不需要 'state'。 你可以自上而下,或自下而上构建静态版本。就是说,你可以从层级结构的顶层开始构建组件(FilterableProductTable),也可以从层级结构的底层开始构建组件(ProductRow)。在更简单的例子中,通常自上而下建立更简单,在大的项目中,自下而上建立则更简单一点。 在这个步骤的最后,你将有一个可重用的组件库,用来渲染你的数据模型。这些组件仅有 'render()' 方法,因为这是你应用的一个静态版本。在层级结构顶层的组件(FilterableProductTable)将你的数据模型,定义为 'props' 属性。如果你修改了数据模型,并再次调用了 'ReactDOM.render()',UI将被更新。查看UI如何更新,以及哪些地方进行了修改,是很简单的。因为没有什么复杂的东西。React的 'one-way data flow'(也称作 'one-way binding'),使得一切都模块化,并且运行的最快。 一个小插曲: 在React中,有2种数据模型:'props' 和 'state'。理解2者的不同很关键;如果不确定2者的不同,可查看之前文档中的解释。 Step 3: Identify The Minimal (but complete) Representation Of UI State - 第三步:确定表示UI状态的最小(但是完整的)集合 为了使你的用户界面交互,你需要能够触发对你的底层数据模型的修改。React使用 'state' 让这个变的很容易。 为了正确构建应用程序,首先你需要考虑应用需要的可变的 'state' 状态的最小集合。关键是 'DRY':Don't Repeat Yourself(不要重复)。找出你应用所需的状态的绝对的最小集合,并计算所有你需要的东西。例如,你正在构建一个 'TODO' 列表,那就仅定义一个 'TODO' 列表条目的数组;不要为计数定义一个单独的 'state' 变量。替代的,当你想渲染 'TODO' 个数时,直接使用 'TODO' 数组的长度。 考虑我们示例应用的所有数据。我们有: 1.产品的原始列表 2.用户已经输入的搜索内容 3.多选框的值 4.产品的筛选列表 让我们依次讨论,找出哪个应该被定义为 'state'。简单地问3个问题,关于每个数据: 1.它是否通过父组件的 'props' 传递?如果是,它可能不应该被定义为 'state'。 2.它是否随着时间保持不变?如果是,它可能不应该被定义为 'state'。 3.在你的组件中,它能不能基于其他 'state' 或 'props' 来计算?如果是,它可能不应该被定义为 'state'。 产品的原始列表,作为一个 'props' 被传入,因此不是 'state'。搜索内容和多选框,貌似可被定义为 'state',因为它们随着时间改变,并且不能通过其他一切来计算。最后,产品的筛选列表不是 'state',因为:它可以通过原始的产品列表和搜索文本或多选框的值,来计算。 因此最终,我们的 'state' 是: 1.用户已经输入的搜索内容 2.多选框的值 Step 4: Identify Where Your State Should Live - 第四步:识别 'state' 应该位于哪里 class ProductCategoryRow extends React.Component { render() { return (<tr><th colSpan="2">{this.props.category}</th></tr>); } } class ProductRow extends React.Component { render() { 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> ); } } class ProductTable extends React.Component { render() { var rows = []; var lastCategory = null; this.props.products.forEach((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; }); return ( <table> <thead> <tr> <th>Name</th> <th>Price</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } } class SearchBar extends React.Component { render() { 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> ); } } class FilterableProductTable extends React.Component { constructor(props) { super(props); this.state = { filterText: '', inStockOnly: false }; } render() { 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' 的最小集合。接下来,我们需要定义,哪些组件使这些状态改变(哪些组件拥有这些 'state',也就是 'state' 定义在哪个组件内)。 记住:React中,数据是随着组件层级结构自上而下的单向数据流。可能不会马上明确哪个组件应该拥有哪些 'state'。这往往是新手理解中最具挑战性的一部分,因此通过下面的步骤来弄明白它: 1.对于应用程序的每个 'state': 1>识别基于 'state' 的每个组件(哪些组件需要state) 2>寻找一个拥有这些 'state' 的公共组件(层级结构中,所有需要 'state' 的组件的一个公共父组件) 3>公共组件或在层级结构上,更高层次上的另一个组件,应该拥有 'state' 4>如果你不能找到一个组件,拥有这个 'state',创建一个新组件仅仅为了拥有这个 'state',并且将这个新组件添加到公共组件的上层。 2.让我们针对我们的应用来使用这个策略: 1>'ProductTable' 需要基于 'state' 来过滤产品列表,'SearchBar' 需要基于 'state' 来展示 '搜索文本state' 和 '多选框state'  2>拥有这些 'state' 的公共组件是 'FilterableProductTable' 3>概念上,'过滤文本' 和 '多选框值' 这2个 'state' 位于 'FilterableProductTable' 组件上,是有意义的。 我们已经决定我们的state位于 'FilterableProductTable' 组件。首先,添加一个属性:this.state = {filterText: '', inStockOnly: false} 到 'FilterableProductTable' 的 'constructor' 方法,表示应用的初始化 'state'。接着,传递 'filterText' 和 'inStockOnly' 给 'ProductTable' 和 'SearchBar' 组件,作为它们的属性。最后,使用这些属性,在 'ProductTable' 来过滤行,以及在 'SearchBar' 中设置表单字段的值。 你可以看到你的应用将如何工作:设置 'filterText' 为 'ball',并刷新你的应用。你将看到数据表正确的更新了。 Step 5: Add Inverse Data Flow - 第五步:添加一个反向数据流 class ProductCategoryRow extends React.Component { render() { return (<tr><th colSpan="2">{this.props.category}</th></tr>); } } class ProductRow extends React.Component { render() { 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> ); } } class ProductTable extends React.Component { render() { var rows = []; var lastCategory = null; this.props.products.forEach((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; }); 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.handleChange = this.handleChange.bind(this); } handleChange() { this.props.onUserInput( this.refs.filterTextInput.value, this.refs.inStockOnlyInput.checked ); } render() { 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> ); } } class FilterableProductTable extends React.Component { constructor(props) { super(props); this.state = { filterText: '', inStockOnly: false }; this.handleUserInput = this.handleUserInput.bind(this); } handleUserInput(filterText, inStockOnly) { this.setState({ filterText: filterText, inStockOnly: inStockOnly }); } render() { 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') ); 到目前为止,我们已经创建了一个应用,。现在是时候以另一种方式支持数据流:在层级结构中的深层表单组件,需要在 'FilterableProductTable' 中更新 'state'。 React的数据流很明确,使得理解你的应用程序是如何工作的变的很容易,但相比于传统的双向数据绑定,它确实需要输入更多。 在示例中的当前版本中,如果你尝试输入或者勾选多选框,你将看到React会忽略你的输入。这是故意的,因为我们输入的值,总是等于从 'FilterableProductTable' 传入的 'state' 值。 让我们思考我们想要发生什么。我们想要确保:每当用户更改了表单,我们更新 'state' 来反映用户输入。因为组件仅应该更新它们自己的 'state', 'FilterableProductTable' 将传递一个回调函数给 'SearchBarla',来触发 'state' 更新。我们可以在表单元素上,使用 'onChange' 事件来通知它。通过 'FilterableProductTable' 传递的回调,将调用 'setState()' 方法,应用将被更新。 虽然这听起来很复杂,但它确实只是几行代码。而且贯穿整个应用中,你的数据是如何流动,是很明确的。 And That's It - 这就是它 希望,通过这篇教程让你知道,使用React如何来创建组件和应用。虽然相比于你之前使用过的其他类库,React可能需要写更多,但是记住:代码的阅读远比书写更重要,并且阅读通过React创建的模块化、清晰的代码是极其容易的。当你开始建立大型的组件库时,你将会欣赏这个明确化和模块化,重用性代码,你的代码行将减少!
    转载请注明原文地址: https://ju.6miu.com/read-676520.html

    最新回复(0)