首先编写一下咱们的公共组件react
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
api
// 商品列表
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做为一个无状态组件,只作他的展现。bash
数据交互经过props传递,点击+-
会改变购物车里的数据。函数
如今需求来了,双12来了(就在昨日),全部商品8折优惠。ui
这意味着咱们须要修改goodsData中全部商品的价格。this
这并不难,我叶良辰有100种方法能够修改商品数据。找个可行的生命周期,好比componentDidMount
中修改list组件state.goodsData
就好了。spa
若是每次修改都直接修改goodList组件,也是能够的,大不了多传几个props来判断须要打折仍是修改商品名称等等。设计
可是有些需求是交叉的,若是一直这样写,长此以往组件会变得愈来愈臃肿,最后爆炸。code
好的解决方案应该是goodsList不去动他,外加一层来进行包装,实现咱们的逻辑。
这样既保证了goodsList的纯
,又能实现逻辑的复用。可谓一举两得。
用两种组件设计模式能够帮助到咱们。
咱们调用公共组件的方法以下:
<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
组件毫无反作用,从而达到咱们的目的。
编写咱们的高阶组件以下:
const BrandGoodsList = (Component, goodsData) => {
// 品牌商品逻辑
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>
复制代码
以为有帮助的点个赞,甚至能够关注一波哦~