Let’s start by writing our common component

Individual commodity component (commodity component: display price, purchase quantity)

goodsItem.js

// Single commodity 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>export default GoodsItem;
Copy the code

Item List component (circularly displaying items in the library)

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, The '-')} /> )} </div> ); }};export default GoodsList;
Copy the code

This is what we do when we write components, list wraps item, loop through item. The data is stored in the List component, and the item, as a stateless component, only does its presentation.

The data interaction is passed through props, and clicking +- changes the data in the shopping cart.

Now the demand is coming, double 12 is coming (just yesterday), 20% off all products.

This means we need to modify the prices of all items in goodsData.

This is not difficult, I have 100 ways to modify the product data. Find a viable lifecycle, such as modifying the List component state.goodsData in componentDidMount.

If you were to modify the goodList component directly for each change, you could pass several props to determine if you need to discount or change the product name, etc.

But some requirements are overlapping, and if you keep writing like this, over time the components will become bloated and explode.

A good solution would be for goodsList to leave it alone and add another layer to wrap it around and implement our logic.

This not only ensures the purity of goodsList, but also realizes the reuse of logic. Kill two birds with one stone.

Using two component design patterns can help.

RenderProps mode

RenderProps is a way of writing functions as components using the props. Children API of the component.

We call the public component as follows:

<GoodsList goodsData={goodsData} />
Copy the code

We implement the discount item component with the renderProps pattern:

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

As you can see, the child component of DiscountedGoodsList is a function, so how does a function render as a component?

Let’s look at the DiscountedGoodsList component again:

Const DiscountedGoodsList = props => {// 20% discount logic constsetRenderPropsData = (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);
      returnarray; } []);return renderPropsData;
  }

  let goodsData = setRenderPropsData(props.goodsData);

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

The purpose of setRenderPropsData is to implement the 20% discount logic, adjust the price of all goods.

Then call the props. Children API to get the function we wrote above.

Children is a reference to the function (data) =>
.

The processed data, goodsData, is executed as an argument, and the
component is returned, which is the renderProps mode.

In the future, we need to call the discounted item list component, just call DiscountedGoodsList.

The pattern of renderProps achieves this by sharing logic with no side effects on the GoodsList component.

HOC (high order component) mode

A higher-order component is a function that takes component as an argument and returns a processed component.

Write our higher-order component as follows:

Const BrandGoodsList = (Component, goodsData) => {// Brand logic constsetRenderPropsData = (data) => {
    let renderPropsData = data.reduce((array, goods) => {
      let obj = {};
      for (let k in goods) {
        obj[k] = k === 'name' ? goods[k] + '[Brand]' : goods[k];
      }
      array.push(obj);
      returnarray; } []);return renderPropsData;
  }

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

The logic of the BrandGoodsList component is to label the product name with the brand name to differentiate the product.

Calling higher-order components is simpler: {BrandGoodsList(GoodsList, goodsData)} executes the return component directly, then renders it.

Having implemented both models, we now use them together to achieve a component that is both discounted and branded goods.

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

It’s nice, isn’t it? Anytime you want to separate, anytime you want to join. It’s the highly cohesive, low-coupling self.

Finally, take a look at the full call:

<div className="App"<GoodsList goodsData={goodsData} /> <br />  <DiscountedGoodsList goodsData={goodsData}> {(data) => <GoodsList goodsData={(data)} />} </DiscountedGoodsList> <br /> Brand goods list component (high-level component mode implementation) : {BrandGoodsList(GoodsList, goodsData)} <DiscountedGoodsList goodsData={goodsData}> {(data) => BrandGoodsList data)} </DiscountedGoodsList> </div>Copy the code

Conclusion:

1. The core of the renderProps pattern is the use of props. Children.

2. Writing higher-order components looks more comfortable and popular.

3. The problems solved by the two modes: reuse logic and do not pollute the underlying components.

Click a “like” if you think it’s helpful, and you can even follow a wave