完整程式碼可檢視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、兩種模式解決的問題:複用邏輯、不汙染底層元件。
覺得有幫助的點個贊,甚至可以關注一波哦~