当前位置: 首页 > news >正文

React组件设计模式(一)

完整代码可查看github,这里截取的代码不影响理解就行。

页面效果可查看gitPage

首先编写一下我们的公共组件

单个商品组件(商品组件:展示价格、购买数量)

goodsItem.js

// 单个商品
import React from 'react';
const GoodsItem = props => {
  const { goods: {name, num, price}, handleSub, handleAdd } = props;
  return <div className="goods-item">
    {name}  
    <button onClick={() => handleSub()}>-</button>
    <span>{num}</span>
    <button onClick={() => handleAdd()}>+</button>
    价格:{price}
  </div>
};
export default GoodsItem;

商品列表组件(循环展示库中的商品)

goodList.js

// 商品列表

import React from 'react';
import GoodsItem from './goodsItem';
class GoodsList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      goodsData: []
    }
  }
  componentDidMount() {
    const { goodsData } = this.props;
    this.setState({ goodsData: goodsData});
  }
  handleAddorSub(id, type) {
    let { goodsData } = this.state;
    let newGoods = goodsData.reduce((newData, goods) => {
      if (goods.id === id) {
        goods.num = type === '+' ? goods.num + 1 : goods.num - 1;
      }
      newData.push(goods);
      return newData;
    }, [])
    this.setState({ goodsData: newGoods })
  }
  render() {
    const { goodsData } = this.state;
    return (
      <div className="goods-list">
        {goodsData.map(goods =>
          <GoodsItem 
            key={goods.id} 
            goods={goods}
            handleAdd={() => this.handleAddorSub(goods.id, '+')}
            handleSub={() => this.handleAddorSub(goods.id, '-')}
          />
        )}
      </div>
    );
  }
};

export default GoodsList;

我们一般编写组件,都会这么去做,list包裹item,循环展示item。数据放在list组件中,item作为一个无状态组件,只做他的展示。

数据交互通过props传递,点击+-会改变购物车里的数据。

现在需求来了,双12来了(就在昨日),所有商品8折优惠。

这意味着我们需要修改goodsData中所有商品的价格。

这并不难,我叶良辰有100种方法可以修改商品数据。找个可行的生命周期,比如componentDidMount中修改list组件state.goodsData就行了。

如果每次修改都直接修改goodList组件,也是可以的,大不了多传几个props来判断需要打折还是修改商品名称等等。

但是有些需求是交叉的,如果一直这样写,久而久之组件会变得越来越臃肿,最后爆炸。

好的解决方案应该是goodsList不去动他,外加一层来进行包装,实现我们的逻辑。

这样既保证了goodsList的,又能实现逻辑的复用。可谓一箭双雕。

用两种组件设计模式可以帮助到我们。

一. renderProps 模式

renderProps其实是利用组件的props.children api,将函数当成组件的一种写法。

我们调用公共组件的方法如下:

<GoodsList goodsData={goodsData} />

我们用renderProps模式实现打折商品组件:

<DiscountedGoodsList goodsData={goodsData}>
  {(data) => <GoodsList goodsData={(data)} />}
</DiscountedGoodsList>

可以看到,DiscountedGoodsList的子组件是一个函数,那么一个函数是怎么渲染成组件的?

再来看看DiscountedGoodsList组件的代码:

const DiscountedGoodsList = props => {
  // 8折优惠逻辑
  const setRenderPropsData = (data) => {
    let renderPropsData = data.reduce((array, goods) => {
      let obj = {};
      for (let k in goods) {
        obj[k] = k === 'price' ? (goods[k] * .9).toFixed(2) : goods[k];
      }
      array.push(obj);
      return array;
    }, []);
    return renderPropsData;
  }

  let goodsData = setRenderPropsData(props.goodsData);

  return (
    <React.Fragment>
      {props.children(goodsData)}
    </React.Fragment>
  );
}

setRenderPropsData的作用是实现8折优惠逻辑,将所有商品价格调整。

然后调用props.children这个api,得到在上面我们编写的函数。

props.children也就是函数(data) => <GoodsList goodsData={(data)} />的引用。

将处理后的数据goodsData作为参数执行,最终返回<GoodsList />组件,这就是renderProps模式。

以后我们需要调用价格优惠的商品列表组件,直接调用DiscountedGoodsList即可。

renderProps的模式实现了逻辑的共用,且对GoodsList组件毫无副作用,从而达到我们的目的。

二. HOC(高阶组件)模式

所谓的高阶组件,其实就是一个函数,该接受component为参数,返回一个处理后的component。

编写我们的高阶组件如下:

const BrandGoodsList = (Component, goodsData) => {
  // 8折优惠逻辑
  const setRenderPropsData = (data) => {
    let renderPropsData = data.reduce((array, goods) => {
      let obj = {};
      for (let k in goods) {
        obj[k] = k === 'name' ? goods[k] + '【品牌】' : goods[k];
      }
      array.push(obj);
      return array;
    }, []);
    return renderPropsData;
  }

  let brandGoodsData = setRenderPropsData(goodsData);
  return <Component goodsData={brandGoodsData} />
}

BrandGoodsList组件的逻辑就是给商品名称加上【品牌】的标示,区分商品。

高阶组件的调用比较简单:{BrandGoodsList(GoodsList, goodsData)} 直接执行返回组件,然后渲染。

实现了两种模式,现在我们将他们一起用,实现一个既打折,又是品牌商品的组件。

<DiscountedGoodsList goodsData={goodsData}>
  {(data) => BrandGoodsList(GoodsList, data)}
</DiscountedGoodsList>

挺舒服的吧,随时分离,随时结合。正是高内聚、低耦合本人啊。

最后,完整的调用看一下:

<div className="App">
    基本商品列表组件:
    <GoodsList goodsData={goodsData} />
    <br />

    打8折商品列表组件(renderProps模式实现):
    <DiscountedGoodsList goodsData={goodsData}>
      {(data) => <GoodsList goodsData={(data)} />}
    </DiscountedGoodsList>
    <br />

    品牌商品列表组件(高阶组件模式实现):
    {BrandGoodsList(GoodsList, goodsData)} 
    <br />
    
    既是打折商品,又是品牌商品(两种模式复用)
    <DiscountedGoodsList goodsData={goodsData}>
      {(data) => BrandGoodsList(GoodsList, data)}
    </DiscountedGoodsList>
  </div>

总结:

1、renderProps 模式的核心是props.children的使用。

2、高阶组件的写法看起来更舒服,比较受欢迎。

3、两种模式解决的问题:复用逻辑、不污染底层组件。

觉得有帮助的点个赞,甚至可以关注一波哦~

相关文章:

  • 【技能意志矩阵-skill will matrix】工作中究竟是个人能力更重要,还是我们的积极性更能提高我们的业绩?...
  • Kubernetes-架构路线图
  • libevent的入门学习-库的安装【转】
  • swift - UIWebView 和 WKWebView(iOS12 之后替换UIWebView)
  • jmeter聚合报告详解
  • php中的匿名函数和闭包(closure)
  • nginx rewrite only specific servername to https
  • 闭包总结
  • 浅谈k8s cni 插件
  • Python爬虫--- 1.3 BS4库的解析器
  • Intellij IDEA 部署 Spring Boot / Spring Cloud 应用到阿里云
  • 线程之间调用问题
  • cdn转es5
  • Selenium 2自动化测试实战
  • css控制默认滚动条样式
  • docker-consul
  • Fundebug计费标准解释:事件数是如何定义的?
  • Mysql数据库的条件查询语句
  • Python爬虫--- 1.3 BS4库的解析器
  • React16时代,该用什么姿势写 React ?
  • Vue2 SSR 的优化之旅
  • webpack+react项目初体验——记录我的webpack环境配置
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 关于for循环的简单归纳
  • 前端性能优化--懒加载和预加载
  • 前嗅ForeSpider教程:创建模板
  • 实现简单的正则表达式引擎
  • 通信类
  • 小试R空间处理新库sf
  • 学习使用ExpressJS 4.0中的新Router
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 06-01 点餐小程序前台界面搭建
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • #大学#套接字
  • (1)(1.11) SiK Radio v2(一)
  • (1)Nginx简介和安装教程
  • (C语言)二分查找 超详细
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (算法)N皇后问题
  • (转)shell调试方法
  • .Net 8.0 新的变化
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中(Constrained Execution Regions)
  • .Net小白的大学四年,内含面经
  • @Autowired注解的实现原理
  • @media screen 针对不同移动设备
  • @staticmethod和@classmethod的作用与区别
  • [BZOJ 4598][Sdoi2016]模式字符串
  • [BZOJ] 1001: [BeiJing2006]狼抓兔子
  • [C语言]——柔性数组