React从入门到入土系列3-使用React构建你的应用

news2024/11/26 15:30:07

这是我自己系统整理的React系列博客,主要参考2023年3月开放的最新版本react官网内容,欢迎你阅读本系列内容,希望能有所收货。
本文是该系列的第3篇文章,阅读完本文后你将收获:

  • 如何使用React逐步构建你的应用
  • 了解props和state的概念

1 需求澄清

假如你现在是一个React项目的开发负责人,设计师已经根据需求设计出了一个UI界面,你需要按照需求开发如下界面:
在这里插入图片描述
这是一个菜单应用,能够展示商品列表及其价格,如果某商品没有库存了将以红色显示;另外,用户还可以根据上方的搜索框对结果进行过滤,或者勾选单选框以显示还有库存的商品。
本文将以这样一个需求为例,梳理React应用的开发步骤,帮助你快速上手。

2 根据UI界面划分组件

React应用是使用一个一个的组件拼装而成的,因此第一步要做的就是根据UI设计稿将页面拆分成一个个的Component。如下图所示,最外层的Component就是最终被调用的组件(FilterableProductTable),然后以该组件为根节点拆分成若干个子组件。
在这里插入图片描述

3 根据设计稿编写一个静态版本的代码

接下来就根据前一个阶段的分析结果,创建多个组件并将结果展示出来。注意,在这个阶段你可以先不考虑组件的交互性,只需要按照设计稿编写静态代码,能够演示的效果即可。因此你需要编写:

  • FilterableProductTable.tsx
  • SearchBar.tsx
  • ProductTable.tsx
  • ProductCategoryRow.tsx
  • ProductRow.tsx
    以上五个组件的静态代码,组件中呈现的数据可以不考虑接入真实的接口,可以直接使用Mock或者静态数据。由于组件之间存在嵌套关系,因此不可避免地,你需要使用props将数据从父组件传递给子组件,如:在ProductTable组件中肯定已经存储了商品列表products,然后该组件又将每个商品的数据传递给其子组件ProductRow或者ProductCategoryRow,这种时候就需要使用props了。
    编写好的静态呈现代码可能如下所示:
/**
 * 这是React.dev官网Quick Start中的Demo应用
 * 对应章节:https://react.dev/learn/thinking-in-react
 * 
 * @author Howard Wonnaut
 * @date 2023-4-9
 */

import "./FilterableProductTable.css";

export type Product = {
    category: string;
    price: string;
    stocked: boolean;
    name: string;
}

function ProductCategoryRow({ category }: {category: string}) {
  return (
    <tr>
      <th colSpan={2}>
        {category}
      </th>
    </tr>
  );
}

function ProductRow({ product }: {product: Product}) {
  const name = product.stocked ? product.name :
    <span style={{ color: 'red' }}>
      {product.name}
    </span>;

  return (
    <tr>
      <td>{name}</td>
      <td>{product.price}</td>
    </tr>
  );
}

function ProductTable({ products }: {products: Array<Product>}) {
  const rows: any = [];
  let lastCategory: string | null = null;

  products.forEach((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>
  );
}

function SearchBar() {
  return (
    <form>
      <input type="text" placeholder="Search..." />
      <label>
        <input type="checkbox" />
        {' '}
        Only show products in stock
      </label>
    </form>
  );
}

function FilterableProductTable({ products }: {products: Array<any>}) {
  return (
    <div>
      <SearchBar />
      <ProductTable products={products} />
    </div>
  );
}

export default FilterableProductTable;

4 分析组件间的逻辑关系,给组件设置state状态

在前面的小节里,我们已经使用props将products自顶向下进行传递,并且将页面渲染了出来。接下来,我们需要考虑如何让这个页面可以交互起来,即根据输入框的值对结果进行过滤、根据单选框的状态对没有库存的结果进行显示/隐藏切换,为了达成这个目的,我们就需要使用state来存储对应的状态了。

我们可以根据如下三个问题来确定是否需要使用state来存储数据:

  1. 随着时间的推移,数据是否保持不变?如果是,那么不需要state
  2. 数据是否是父组件通过props传递过来的?如果是,那么不需要state
  3. 是否能够根据已经存在的state或者props计算出该数据?如果是,那么不需要state

根据上面的原则,我们梳理一下这个示例应用程序中的数据有哪些,以及是否需要使用state:

  1. 最外层的产品列表products:直接通过props传入即可,不需要state
  2. 用户在搜索框输入的字符:会随着用户的输入而改变,且不能被计算出来,因此需要state
  3. 单选框的状态:会随着用户的操作而改变,且不能被计算出来,因此需要state
  4. 产品列表的筛选结果:可以根据products,输入框的字符和单选框状态计算得到,因此不需要state

基于上述分析,我们清楚了:输入框和单选框需要使用state保存其状态,其余的数据则不需要。

Props和State的对比
到这里,我们已经对该示例程序中的props和state进行了梳理,现将这二者的区别总结如下:

  • Props类似你像函数传入的参数,将父组件的数据传递给子组件;
  • State类似于一个组件的记忆,其允许组件保存某些信息,并且在用户进行交互操作之后更新存储的数据。例如:一个Button组件能够通过state存储其hover状态isHovered

接下来,我们需要再分析一下,应该将state放在哪个组件里面。由于搜索框和单选框都在SearchBar组件中,那么能不能直接把state放在该组件中呢?答案是不能,因为对于搜索框和单选框进行的交互操作都会影响ProductTable组件的呈现结果,如果将state保存在SearchBar中,ProductTable组件将无法及时感知到用户的操作行为,从而导致数据更新异常。因此,需要将state存放在SearchBar和ProductTable组件的公共父组件FilterableProductTable中,然后使用props将filterText和inStockOnly传递给子组件,此时,对应的代码为:

/**
 * 这是React.dev官网Quick Start中的Demo应用
 * 对应章节:https://react.dev/learn/thinking-in-react
 *
 * @author Howard Wonnaut
 * @date 2023-4-9
 */

import { useState } from 'react'
import './FilterableProductTable.css'

export type Product = {
  category: string
  price: string
  stocked: boolean
  name: string
}

function ProductCategoryRow({ category }: { category: string }) {
  return (
    <tr>
      <th colSpan={2}>{category}</th>
    </tr>
  )
}

function ProductRow({ product }: { product: Product }) {
  const name = product.stocked ? (
    product.name
  ) : (
    <span style={{ color: 'red' }}>{product.name}</span>
  )

  return (
    <tr>
      <td>{name}</td>
      <td>{product.price}</td>
    </tr>
  )
}

function ProductTable({
  products,
  filterText,
  inStockOnly,
}: {
  products: Array<Product>
  filterText: string
  inStockOnly: boolean
}) {
  const rows: any = []
  let lastCategory: string | null = null

  products.forEach((product) => {
    // 如果只显示有库存的数据,并且当前商品无库存,直接不显示
    if (inStockOnly && !product.stocked) {
      return
    }

    // 如果过滤文本不在当前商品名称中存在,不显示该商品
    if (
      filterText &&
      product.name
        .toLocaleLowerCase()
        .indexOf(filterText.toLocaleLowerCase()) === -1
    ) {
      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>
  )
}

function SearchBar({
  filterText,
  inStockOnly,
}: {
  filterText: string
  inStockOnly: boolean
}) {
  return (
    <form>
      <input type="text" value={filterText} placeholder="Search..." />
      <label>
        <input type="checkbox" checked={inStockOnly} /> Only show products in
        stock
      </label>
    </form>
  )
}

function FilterableProductTable({ products }: { products: Array<any> }) {
  const [filterText, setFilterText] = useState('')
  const [inStockOnly, setInStockOnly] = useState(false)

  return (
    <div>
      <SearchBar filterText={filterText} inStockOnly={inStockOnly} />
      <ProductTable
        filterText={filterText}
        inStockOnly={inStockOnly}
        products={products}
      />
    </div>
  )
}

export default FilterableProductTable

此时,只完成了数据自顶向下的传递,但是用户在SearchBar中的操作还没有传递到FilterableProductTable组件中,因此需要完善数据向上传递的链路,修改了FilterableProductTable和SearchBar组件的代码逻辑:


function SearchBar({
  filterText,
  inStockOnly,
  onFilterTextChange,
  onInStockOnlyChange,
}: {
  filterText: string
  inStockOnly: boolean
  onFilterTextChange: Function
  onInStockOnlyChange: Function
}) {
  return (
    <form>
      <input
        type="text"
        value={filterText}
        placeholder="Search..."
        onChange={(e) => onFilterTextChange(e.target.value)}
      />
      <label>
        <input
          type="checkbox"
          checked={inStockOnly}
          onChange={(e) => onInStockOnlyChange(e.target.checked)}
        />{' '}
        Only show products in stock
      </label>
    </form>
  )
}

function FilterableProductTable({ products }: { products: Array<any> }) {
  const [filterText, setFilterText] = useState('')
  const [inStockOnly, setInStockOnly] = useState(false)

  return (
    <div>
      <SearchBar
        filterText={filterText}
        inStockOnly={inStockOnly}
        onFilterTextChange={setFilterText}
        onInStockOnlyChange={setInStockOnly}
      />
      <ProductTable
        filterText={filterText}
        inStockOnly={inStockOnly}
        products={products}
      />
    </div>
  )
}

最终的呈现效果如下,能够只显示有库存的商品:
在这里插入图片描述
能够根据输入文本对结果进行过滤:
在这里插入图片描述
本文完,希望能够对你有所帮助~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/429226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《分布式商业》

读完《分布式商业&#xff1a;数字化时代的新商业变革》&#xff0c;说实话&#xff0c;如果读者没有技术研发背景&#xff0c;读完此书&#xff0c;是很难在技术和商业层面引起共鸣。我甚至觉得&#xff0c;这本书就是写给技术类岗位的同学看的&#xff1b;而写这本书的人&…

Softing工业边缘产品的新功能助力工业数据集成到IT解决方案中

Softing的edgeConnector和edgeAggregator产品在3.40版本中新增了一系列功能&#xff0c;使数据集成到IT解决方案变得更加容易。 基于Docker的edgeConnector产品系列支持访问SIMATIC S7、SINUMERIK 840D和Modbus TCP等控制器中的过程数据。同样基于Docker的edgeAggregator产品可…

柔性数组【结构体和动态内存的结合】

全文目录前言柔性数组的定义语法柔性数组的特点柔性数组的使用柔性数组的优势前言 很多人可能没有听过柔性数组这个概念&#xff0c;但是在C99中柔性数组是确实存在的。我个人感觉有点像动态内存和结构体的结合。 柔性数组的定义语法 结构中的最后一个元素允许是未知大小的数…

一起学 WebGL:绘制三角形

大家好&#xff0c;我是前端西瓜哥。画了好几节课的点&#xff0c;这次我们来画三角形了。 三角形可太重要了&#xff0c;再复杂的三维模型都是由一个个小三角形组合而成&#xff0c;越多越精细越真实。 绘制三角形 这次绘制三角形&#xff0c;要绘制的点就有三个了&#xf…

C语言之 单链表1(simply linked list)

单链表 链表优点&#xff1a; 1.按需申请空间&#xff0c;需要就申请&#xff0c;不需要就释放 2.头部或中间插入数据&#xff0c;不需要挪动数据 3.不存在空间浪费 缺点&#xff1a; 1.每次存放一个数据&#xff0c;到要存一个指针去链接后面的数据节点 2.不支持随机访问&a…

让你的ChatGPT更加强大——200+小白用来解锁ChatGPT高级功能的提示(Prompts)

让你的ChatGPT更加强大——200小白用来解锁ChatGPT高级功能的提示&#xff08;Prompts&#xff09;使用说明标签筛选关键词搜索展示区复制语言切换常见问题为什么提示词用英文&#xff1f;中文搜索出错输出虚假信息提示词不好用为什么执着于 ChatGPT&#xff1f;最后参考博客其…

Vue.js 2.0 单文件组件

Vue.js 2.0 单文件组件介绍 在很多Vue项目中&#xff0c;我们使用 Vue.component 来定义全局组件&#xff0c;紧接着用 new Vue({ el: #container }) 在每个页面内指定一个容器元素。 这种方案在只是使用 JavaScript 增强某个视图的中小型项目中表现得很好。然而在更复杂的项…

对象树、QT的坐标系、信号和槽机制

目录 1、QT中什么是对象树 2、QT的坐标系 3、信号和槽机制 3.1、信号槽的理解 3.2、信号槽的工作原理 3.3、信号槽的使用 3.3.1、系统的信号和槽 3.3.2、自定义信号和槽函数 3.3.3、信号和槽函数之间的参数传递 3.3.4、信号和槽的注意 1、QT中什么是对象树 在创建 QO…

重构·改善既有代码的设计.04之重构手法(下)完结

1. 前言 本文是代码重构系列的最后一篇啦。前面三篇《重构改善既有代码的设计.01之入门基础》、《重构改善既有代码的设计.02之代码的“坏味道”》、《重构改善既有代码的设计.03之重构手法&#xff08;上&#xff09;》介绍了基础入门&#xff0c;代码异味&#xff0c;还有部…

如何在ubuntu上搭建minio

由于腾讯的对象存储服务器&#xff08;COS&#xff09;的半年免费试用期已过&#xff0c;所以寻思鼓捣一下minio&#xff0c;试着在自己的服务器上搭建一套开源的minio对象存储系统。 单机部署基本上有以下两种方式。 一、直接安装 最基础的一种单机安装&#xff0c;这里不做…

记录-js基础练习题

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 隔行换色(%): window.onload function() {var aLi document.getElementsByTagName(li);for(var i 0; i < aLi.length; i){if(i%2 1){aLi[i].style.background #bfa;}} } <ul><li>…

微搭低代码学习之基础操作

低代码开发平台&#xff0c;是一种方便产生应用程序的平台软件&#xff0c;软件会开发环境让用户以图形化接口以及配置编写程序&#xff0c;而不是用传统的程序设计作法。此平台可能是针对 某些种类的应用而设计开发的&#xff0c;例如数据库、业务过程、以及用户界面。这类平台…

STM32锁芯片,解锁方法(J-LINK)

在烧写STM32时&#xff0c;不小心把usb口给弄掉了&#xff0c;然后就尴尬了&#xff0c;芯片被锁了。 烧写的时候报错&#xff0c;如下&#xff1a; 然后就搜了一下&#xff0c;大致有两个方法&#xff0c; 第一种需要通过接3.3V到板子上BOOT0重新上电第二种是直接通过软件…

Golang语言Windows环境搭建(Visual Studio Code)

一、Golang语言简介 二、Windows环境搭建 1、软件下载 Golang语言安装包官网下载地址&#xff1a;https://golang.google.cn/dl/ Visual Studio Code编辑器下载&#xff1a;https://code.visualstudio.com/ 2、Golang安装及环境变量配置 2.1 Golang语言安装包 双击安装…

可配置物料-文章资料分享

可配置物料项目一般很少用到&#xff0c;用到就是要命&#xff0c;推荐一下之前查资料收集的资料&#xff0c;分享给大家。感谢这位大佬收集的文章&#xff01; Variant Configuration (LO-VC) - Product Lifecycle Management - Support Wikihttps://archive.sap.com/document…

5V的LDO电源的WCCA分析-可靠性分析计算过程

WCCA(WorstCase Circuit Analysis)分析方法是一种电路可靠性分析设计技术&#xff0c;用来评估电路中各个器件同时发生变化时的性能&#xff0c;用于保证设计电路在整个生命周期的都可以可靠工作。通过WCCA分析&#xff0c;验证在上述参数在其容差范围内发生变化时&#xff0c;…

C++模板基础(八)

数值模板参数与模板模板参数 ● 模板可以接收&#xff08;编译期常量&#xff09;数值作为模板参数 – template class Str; template<int a> int fun(int x) {return x a; }int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);std::cout << fun…

相机SD卡无法读取提示格式化 相机SD卡无法读取怎么修复

相机SD卡中储存着的照片和视频&#xff0c;承载着我们美好的回忆。因为相机SD卡的容量有限&#xff0c;我们会定期对SD卡中的数据进行云盘备份&#xff0c;然后清理相机SD卡中的数据。在打开相机SD卡时&#xff0c;可能会遇到SD卡无法读取的情况。那么&#xff0c;相机SD卡无法…

Umi‘s Friends 冠名 VC MeetUP 酒会,圆满举办!

香港加密新政的整体目的是令虚拟资产交易明确化和合法化&#xff0c;通过不断完善的监管框架&#xff0c;促进香港虚拟资产行业的可持续和负责任地发展。在加强合规和持牌经营的监管思路下&#xff0c;长期审慎合规经营的老牌机构和项目&#xff0c;显然将获得先发优势。随着香…

开放式蓝牙耳机哪个好,分享几款舒适性高的开放式蓝牙耳机

开放式耳机的兴起是近几年来才出现的新概念&#xff0c;开放式耳机也是近几年来才开始流行起来&#xff0c;在我看来开放式耳机的兴起是科技进步的产物。随着蓝牙耳机技术和设备的发展&#xff0c;蓝牙耳机也越来越普及&#xff0c;但是也给用户带来了很多困扰。而开放式耳机就…