【React】React全家桶(六)React哲学


参考链接
React 哲学官方文档

刚使用React的同学有一个通病:拿到UI图后,没有任何思考和设计就去写,想到什么写什么,不合理的代码会出现很多bug,没错说的就是我自己~~~,那么我们就需要学习一下React哲学,看看别人是如何思考构建页面的。

React哲学会引导我们思考如何构建一个应用 , 倡导我们把代码写得更加简洁清晰,更具模块化,这一点在写大型的项目尤为重要,在写代码之前就把大致的结构和涉及的数据结构设计好,会减少 Bug 的产生,减少重构的时间,减少维护的成本。

准备阶段

在我们写代码之前 ,一定需要UI产品设计图后端接口数据

UI产品设计图

在这里插入图片描述

后端接口数据

[
  {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"}
];

第一步:通过UI产品设计图划分组件层级

理清UI产品设计图的需求:

  • 一个展示商品的列表
  • 对商品进行关键字搜索,点击复选框展示商品现货
  • 商品列表包含商品名和价格,商品支持分类显示,其中告罄的商品名为红色显示

划分组件层级:

  • 在设计稿上用方框圈出每一个组件(包括它们的子组件),并且以合适的名称命名(很关键)
  • 将组件当作JS函数考虑,一个组件原则上只能负责一个功能,如果组件功能较多,可以考虑将它拆分成更小的组件
  • UI应与数据模型一一对应 , 若将 UI 分离为组件,其中每个组件需与数据模型的某部分匹配。

不同的颜色划分成不同的组件,可以分成五部分:

  1. FilterableProductTable (橙色): 是整个示例应用的整体
  2. SearchBar (蓝色):接受所有的用户输入
  3. ProductTable (绿色): 展示数据内容并根据用户输入筛选结果
  4. ProductCategoryRow (天蓝色): 为每一个产品类别展示标题
  5. ProductRow (红色): 每一行展示一个产品

在这里插入图片描述

组件的层级划分:

  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow

第二步:用React构建静态页面

编写应用:将编写静态页面和添加交互这两过程分开, 这是因为编写静态页面时需要大量代码,而添加交互时则要考虑大量细节, 将这两个过程分开进行更为合适 ,整个思路也比较清晰。

通过复用编写的组件,使用 props 来进行数据的传递,父组件把数据进行层层的传递,在这个过程中先不使用 state ,因为 state 表示的是会随着时间变化而变化的,所以在交互的过程中使用 。

props和state的区别:

  • props 是传递给组件的(类似于函数的形参),是父组件向子组件传递数据的方式
  • state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量), 代表了随时间会产生变化的数据,应当仅在实现交互时使用
  • 组件可以选择把它的 state 作为 props 向下传递到它的子组件中

构建应用的方法:

  • 自上而下的方法:先写层级最高的组件,如FilterableProductTable 组件,这种比较适合简单的应用
  • 自下而上的方法:先写层级最低的组件,如 ProductRow组件,这种方法比较适合大型的应用构建

静态页面代码示例

// 后端接口JSON数据
const 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'}
];

// 根组件
const FilterableProductTable = () => (
  <div>
    <SearchBar />
    // 给产品列表组件传JSON数据
    <ProductTable products={PRODUCTS} />
  </div>
);

// 搜索组件
const SearchBar = () => (
  <form>
    <input type="text" placeholder="Search..." />
    <p>
      <input type="checkbox" /> Only show products in stock
    </p>
  </form>
);

// 产品列表组件
const ProductTable = ({ products }) => { //从props解构出products(JSON数据)
  const rows = [];
  let lastCategory = 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>
  );
};

// 产品类别标题组件
const ProductCategoryRow = ({ category }) => (
  <tr>
    <td colSpan="2">{category}</td>
  </tr>
);

// 每一行展示一个产品组件
const ProductRow = ({ product }) => {
 // 判断商品是否告罄
  const name = product.stocked ? (
    product.name
  ) : (
    <span style={{ color: "red" }}>{product.name}</span>
  );

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

第三步:确定 state 的最小且完整的集合

想要使你的 UI 具备交互功能,需要有触发基础数据改变的能力 ,React 通过实现 state 来完成这个任务。

state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量), 代表了随时间会产生变化的数据,应当仅在实现交互时使用 。

看一下当前应用有哪些数据

  • 商品的原始数据
  • 用户的搜索数据
  • 复选框是否选中的值
  • 经过筛选后的数据

通过问自己以下三个问题,你可以逐个检查相应数据是否属于 state

  • 数据是否能通过 props 来传递
  • 是否会通过时间而产生改变
  • 是否可以通过其他 state和props计算得到

最后确认,原始数据可以通过 props传递,用户搜索的数据和复选框的值可以作为 state ,筛选后的数据可以通过原始数据和用户搜索数据以及复选框数据计算得来。

最后 state是

  • 用户的搜索数据
  • 复选框是否选中的值

第四步:确定 state 放置的位置

确定 state 放置的位置:

  • React是自上而下的单向数据流,我们应把 state写在这些 state 的组件的共同父组件中
  • 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。

第五步:添加反向数据流

当我们要通过层级较低的组件改变层级较高的组件,就需要通过反向数据流的方式。

React中的反向数据流是需要高层级组件通过 props把改变 state的方法 (回调函数) 传递给低层级组件,低层次组件state改变后将值传给回调函数。

数据驱动UI: 组件化的框架就是数据驱动UI的 ,操作由React完成,你只需关注数据, 因为数据和UI之间建立了联系,通过操作这个数据,UI就能够自动进行变化。

const FilterableProductTable = () => {
  const [filterText, setFilterText] = React.useState("");
  const [inStockOnly, setInStockOnly] = React.useState(false);

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

const SearchBar = ({
  filterText,
  setFilterText,
  inStockOnly,
  setInStockOnly,
}) => {
  const handleProductsSearch = (value) => {
    setFilterText(value);
  };

  const handleStockCheck = (value) => {
    setInStockOnly(value);
  };

  return (
    <form>
      <input
        type="text"
        placeholder="Search..."
        value={filterText}
        onChange={handleProductsSearch}
      />
      <p>
        <input
          type="checkbox"
          value={inStockOnly}
          onChange={handleStockCheck}
        />{" "}
        Only show products in stock
      </p>
    </form>
  );
};

const ProductTable = ({ products, inStockOnly, filterText }) => {
  const rows = [];
  let lastCategory = null;

  products.forEach((product) => {
    if (product.name.indexOf(filterText) === -1) {
      return;
    }
    if (inStockOnly && !product.stocked) {
      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>
  );
};

最后:需要明白组件拆分与封装

模块化:按项目功能需求划分成不同的业务框架(如注册登录、外卖、直播),隔离,复用
组件化:页面上每个独立的区域都可以看做是一个组件,将一个页面拆分成一个个小的功能块,复用、解耦

组件拆分:组件拆分需要考虑布局和交互。需要整体考虑布局和交互,把它的特性分析完毕后,再去考虑拆分。 理解组件化的时候,分析清楚所有的特性,布局特性、交互特性、数据特性,分析完整之后你再去设计,否则会导致你思考出来的拆分可能有问题。拆分有问题,就会导致你实现起来很困难。

组件封装: 把重复出现多的地方进行封装,把共性(一样)的东西封装成组件或者函数,把差异性(不一样)的东西设计成参数。然后差异性的参数与函数里的共性部分组合起来,完成组件封装。

组件封装的作用

  • 简化代码,提高可读性
  • 通过共性和差异性封装,提高代码复用
  • 维护时逻辑清晰,修复时快速定位问题源头。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值