SKU组件(React版)

源码

起因

今天看掘金的时候看到前端SKU算法实现,因为公司也有涉及到SKU的业务,记录一下自己写SKU的一个例子吧,刚好他有提供后端的API接口数据,mock一下干起来,但是在做的时候还是有很多问题的,这里做一下记录

实现效果

mock数据

export const simulatedSku = {id: 2,title: "林间有风自营针织衫",subtitle: "瓜瓜设计,3件包邮",category_id: 12,root_category_id: 2,price: "77.00",img: "",for_theme_img: "",description: null,discount_price: "62.00",tags: "包邮$热门",is_test: true,online: true,sku_list: [{id: 2,price: 77.76,discount_price: null,online: true,img: "",title: "金属灰·七龙珠",spu_id: 2,category_id: 17,root_category_id: 3,specs: [{key_id: 1,key: "颜色",value_id: 45,value: "金属灰"},{key_id: 3,key: "图案",value_id: 9,value: "七龙珠"},{key_id: 4,key: "尺码",value_id: 14,value: "小号 S"}],code: "2$1-45#3-9#4-14",stock: 5},{id: 3,price: 66,discount_price: 59,online: true,img: "",title: "青芒色·灌篮高手",spu_id: 2,category_id: 17,root_category_id: 3,specs: [{key_id: 1,key: "颜色",value_id: 42,value: "青芒色"},{key_id: 3,key: "图案",value_id: 10,value: "灌篮高手"},{key_id: 4,key: "尺码",value_id: 15,value: "中号 M"}],code: "2$1-42#3-10#4-15",stock: 999},{id: 3,price: 66,discount_price: 59,online: true,img: "",title: "橘黄色·灌篮高手",spu_id: 2,category_id: 17,root_category_id: 3,specs: [{key_id: 1,key: "颜色",value_id: 44,value: "橘黄色"},{key_id: 3,key: "图案",value_id: 10,value: "灌篮高手"},{key_id: 4,key: "尺码",value_id: 15,value: "中号 M"}],code: "2$1-42#3-10#4-15",stock: 999},{id: 4,price: 88,discount_price: null,online: true,img: "",title: "青芒色·圣斗士",spu_id: 2,category_id: 17,root_category_id: 3,specs: [{key_id: 1,key: "颜色",value_id: 42,value: "青芒色"},{key_id: 3,key: "图案",value_id: 11,value: "圣斗士"},{key_id: 4,key: "尺码",value_id: 16,value: "大号  L"}],code: "2$1-42#3-11#4-16",stock: 8},{id: 5,price: 77,discount_price: 59,online: true,img:"http://i1.sleeve.7yue.pro/assets/09f32ac8-1af4-4424-b221-44b10bd0986e.png",title: "橘黄色·七龙珠",spu_id: 2,category_id: 17,root_category_id: 3,specs: [{key_id: 1,key: "颜色",value_id: 44,value: "橘黄色"},{key_id: 3,key: "图案",value_id: 9,value: "七龙珠"},{key_id: 4,key: "尺码",value_id: 14,value: "小号 S"}],code: "2$1-44#3-9#4-14",stock: 7}],spu_img_list: [{id: 165,img:"http://i1.sleeve.7yue.pro/assets/5605cd6c-f869-46db-afe6-755b61a0122a.png",spu_id: 2}],spu_detail_img_list: [{id: 24,img: "http://i2.sleeve.7yue.pro/n4.png",spu_id: 2,index: 1}],sketch_spec_id: 1,default_sku_id: 2
};

简单的封装一个SKUCard和SKUGroup

类似于RadioGroup和Radio,我们先封装一个简单的SKU Group和SKU组件,便于状态的统一管理

  • SKU Card的实现,其实很简单,就是在激活的时候和非激活的时候通过状态位,修改css属性,另外onChange的时候将回应的SKU的value进行传递

    • value: sku对应的sku_id
    • label: 显示的sku名称
    • onChange: sku发生变化的时候的回调函数
    • disabled: 禁用标志位
    • activate: 是否为激活模式
export const SkuCard = props => {const { value, label, onChange, disabled, activate, style } = props;const [innerActive, setInnerActive] = useState(activate ?? false);const handleChange = value => () => {if (!disabled) {onChange?.(value, !innerActive);setInnerActive(!innerActive);}};return (<divclassName={disabled ?? false? "disabled": activate ?? innerActive? "activate": "normal"}onClick={handleChange(value)}style={{ ...(style ?? {}) }}>{label}</div>);
};
  • SKU Group: 集中管理SKU的状态,类似于RadioGroup, CheckboxGroup其实都可以模仿这种封装的思路

    • 利用props.children获取各个子元素的ReactElement对象,之后通过cloneElement将父组件内管理状态的onChange方法进行注入(类似于HOC那种感觉),将子组件的activate和onChange方法通过父组件进行管理
    • 封装一些其他自己要用的属性
    • 大功告成
// 定义了Empty,这个Empty对空的时候进行设置
export const Empty = Symbol("empty");export const SkuGroup = props => {const { value, onChange, skuName } = props;const [selected, setSelected] = useState(value);const { children } = props;const _onChange = (value, activate) => {const _value = !activate && selected === value ? Empty : value;setSelected(_value);onChange?.(_value);};const renderGroupChild = (child, index) => {const { props: childProps } = child;return React.cloneElement(child, {...childProps,onChange: _onChange,activate: childProps.value === selected,key: `create-${index}`,style: {...(childProps?.style ?? {}),marginLeft: index === 0 ? 0 : "20px"}});};return (<div className="skuGroup">{skuName && <div className="labelName">{skuName}</div>}{children.map((child, index) => {return child?.type === SkuCard ? renderGroupChild(child, index) : child;})}</div>);
};

SKU组件实现的思路分析

  • 从数据来看,每个商品(SPU)中包含多个SKU,所以要将多个SKU分别提出来整理成这个样子,就是想sku进行归类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JvHm5iHN-1584346216081)(/home/czx/Desktop/Learn/Essays/SkuComponent/img/选区_059.png)]

  • 点击选中某个SKU之后,将选中的SKU的id作为筛选列表中的值,我们需要遍历整个商品列表,筛选出在商品列表中所有满足筛选条件的商品
  • 通过在满足条件的商品列表中进行遍历,得到剩下可选的sku,其余的将sku中的disabled设为true即不能被选择
// 代码中的几个关键变量
// skuList: 商品拥有的所有sku组合的型号(SPU中的所有商品类型)
// sku: 需要显示的sku card
// selectSku: radio显示选中值的[1, 2, 3]// 初始化的时候aviableSku就是所有的商品类目
const _getSku = (aviableSku = []) => {const _sku = {};const _aviableSku = {};// 得到目前可以选择的所有商品的skuaviableSku.forEach(item => {item.forEach(x => {const key = JSON.stringify({ key_id: x.key_id, key: x.key });const value = {value_id: x.value_id,value: x.value,disabled: false};_aviableSku[key]? _aviableSku[key].some(z => z.value_id === x.value_id)? null: _aviableSku[key].push(value): (_aviableSku[key] = [value]);});});// 将SKU中所有不满足aviableSku的东西diabled掉skuList.forEach(item => {// 每个商品item.forEach((x, i) => {// 商品下的每个skuconst key = JSON.stringify({ key_id: x.key_id, key: x.key });const value = {value_id: x.value_id,value: x.value,disabled: !_aviableSku[key].some(item => item.value_id === x.value_id)};_sku[key]? _sku[key].some(z => z.value_id === x.value_id)? null: _sku[key].push(value): (_sku[key] = [value]);});});setMySku(_sku);
};
  • 在选择sku的时候,我们需要确定这个sku是如何改变的,并且调整对应的aviableSku
useEffect(() => {// 利用useRef记录上一次选择sku的状态if (prevSku.current) {// 找到哪一个SKU的值发生了变化const cIndex = findChangeIndex(prevSku.current, selectSku);if (cIndex !== -1) {const changeValue = selectSku[cIndex];let otherCondition = {};const keys = Object.keys(sku);selectSku.forEach((item, index) => {if (changeValue === Empty// 改变值为Empty,说明原来选中,现在取消选中场景? index !== cIndex && item !== Empty// 说明Item是有限定值的: item !== Empty) {// 将限定值保存在otherCondition中// 记录现在的限定状态const key_id = JSON.parse(keys[index])["key_id"];otherCondition[key_id]?.push(item) ??(otherCondition[key_id] = [item]);}});// 通过限定矩阵的值挑选出满足条件的商品类别const aviableSku = skuList.filter(good => {const aviableGood = good.map(sku => {const isInOther = otherCondition[sku.key_id];return isInOther !== undefined? isInOther.includes(sku.value_id): true;});return aviableGood.every(item => item);});_getSku(aviableSku);}} else {_getSku(skuList);}prevSku.current = selectSku;
}, [selectSku]);

学习笔记:SKU组件(React版)相关推荐

  1. [学习笔记] Cordova+AmazeUI+React 做个通讯录 - 单页应用 (With Router)

    [学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章 目录 准备 联系人列表(1) 联系人列表(2) 联系人详情 单页应用 (With Router) 使用 SQLite 传 ...

  2. Mini 容器学习笔记4——组件的生命周期(应用篇)

    Mini容器支持6中生命周期类型: 1. Singleton :单利类型(缺省组件都是单利类型的生命周期,由容器进行托管的) [Test]public void SingletonLifestyleT ...

  3. Mini 容器学习笔记6——组件的获取(应用)

    1. 通过组件Id获取组件实例: [Test]public void GetByIdTest(){ServiceRegistry.Register<Person>("person ...

  4. ASP.Net学习笔记007--ASP.Net Input版自增

    2016/1/18 以前写的课程都没有附上源码,很抱歉! 课程中的源码可以加qq索要:1606841559 技术交流qq1群:251572072 技术交流qq2群:170933152 也可以自己下载: ...

  5. [学习笔记] Cordova+AmazeUI+React 做个通讯录 - 使用 SQLite

    [学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章 目录 准备 联系人列表(1) 联系人列表(2) 联系人详情 单页应用 (With Router) 使用 SQLite 传 ...

  6. Uniapp零基础开发学习笔记(5) -组件入门及容器组件使用

    Uniapp零基础开发学习笔记(5) -组件入门及容器组件使用 按照官网教程学习使用组件,并且重点把容器组件的应用进行了练习. 1.官网关于组件的介绍 组件是视图层的基本组成单元,是一个单独且可复用的 ...

  7. 【QT 5 相关实验-仪表盘-学习笔记-表盘组件练习与使用总结】

    [QT 5 相关实验-仪表盘-学习笔记-表盘组件练习与使用总结] 1.概述 2.实验环境 3.参考资料-致谢 4.自我提升+实验效果 5.代码练习-学习后拆解 (1)头文件部分 (2)绘制事件+绘制表 ...

  8. antd picker 使用 如何_如何基于jsoneditor二次封装一个可实时预览的json编辑器组件?(react版)...

    前言 做为一名前端开发人员,掌握vue/react/angular等框架已经是必不可少的技能了,我们都知道,vue或react等MVVM框架提倡组件化开发,这样一方面可以提高组件复用性和可扩展性,另一 ...

  9. Vue学习笔记05 组件的自定义事件-组件通信-$nextTick-脚手架解决ajax跨域-插槽-过渡动画

    文章目录 Vue学习笔记05 父组件给子组件传值 注意点 子组件给父组件传值 父组件接受子组件的传值 通过函数 组件的自定义事件 事件绑定的第一种写法 @或v-on 事件绑定的第二种写法:使用ref ...

最新文章

  1. 小兵别嚣张,签名算法分析
  2. LeetCode实战:最长有效括号
  3. Linux可执行文件如何装载进虚拟内存
  4. SetFormFullScreen()窗体全屏显示
  5. 你可能不知道的位运算技巧
  6. 程序员修神之路:问世间异步为何物?
  7. 定制操作(传递函数或lambda表达式)
  8. Kafka与.net core(一)安装
  9. paip.python ide 总结最佳实践o4.
  10. python裂缝检测_通过opencv-python检测裂缝
  11. 工作组和域的概念及辨析
  12. c语言if语句后的一对圆括号,在C语言中,紧跟在关键字if后一对圆括号里的表达式()...
  13. 13个offer,8家SSP,谈谈秋招经验
  14. 关于URL重写的实现
  15. 开题报告的国内外研究现状怎么写呢?
  16. 计算机主板提示ahci,win7系统amd计算机主板开ahci.docx
  17. 【LeetCode】括号(有效括号、括号生成、最长有效括号)
  18. 【作业二】结对项目之需求分析与原型模型设计
  19. 构建神经网络模型方法,神经网络建立数学模型
  20. 制作SD卡备份镜像以及还原

热门文章

  1. 计算机毕业设计-基于VUE+SpringBoot+MyBatis+MySQL的学生作业管理系统
  2. Javaweb入职第二天
  3. Planet比Google earth更好用的地图下载神器Basemaps Viewer不用写代码全球高清影像框选下载tif格式
  4. 用装饰器缓存某个函数的结果
  5. 用Python实现的Internet电话软件(P2P-SIP)开源
  6. 【使用java swing制作简易贪吃蛇游戏】软件实习项目二
  7. 2016年云计算会议_云计算会议2012
  8. 用Qt写一个简单的音乐播放器(三):增加界面(播放跳转与音量控制)
  9. 手机横屏怎么设置放大字_手机锁屏可以滚动的文字怎么设置?手把手教你如何设置,太简单了...
  10. “为物联网提供分散的隐私保护医疗区块链”外文翻译——2019年5月份