epicrato
epicrato

Reputation: 8408

How to pass a function as an argument to another function that maps through a list in React?

I am trying to create a function that does nothing else than map through a list. This function is always the same, but the list item fields (Fields) can change. So I am trying to pass Fields to the map function, so that I can reuse the map function (List and Item) and only change Fields.

Here is an example of the code I wrote. It works as is, but I don't know how to store the map function somewhere else where it can be reused between multiple similar components.

const Fields = (props) => {
  return (
    <Fragment>      
      <MediaGroup { ...props } />
      <TextRich   { ...props } /> 
      <LinkGroup  { ...props } />
    </Fragment>
  );
}

const Item = (props) => {
  return (
    <PanelBody title={ `Item ${props.index + 1}` } initialOpen={ false }>
      <PanelRow>
        <RmListItemMarkup { ...props } />
        <Fields           { ...props } />
      </PanelRow>
    </PanelBody>
  );
}

export const List = (props) => {
  const { attr } = props;
  return (
    <Fragment>
      <PanelBody title={ __('Content List') } initialOpen={ true }>
        <AddListItemMarkup { ...props } />
      </PanelBody>

      { (attr && attr.length) ? attr.map((item, index) => <Item key={ index } index={ index } { ...props } />) : null }
    </Fragment>
  );  
}

Ideally I would like List and Item to live it its own file and simply import them from the file that holds Fields. That way I could customize Fields and pass to List without having to repeat List and Item inside the same file. In fact, List and Item could technically be the same function, all I would need to pass is Fields.

Please, let me know if you know any solutions for this. Thanks in advance!

Upvotes: 1

Views: 71

Answers (1)

HMR
HMR

Reputation: 39250

One way to do it is using a HOC:

const ItemType1 = (props) => (
  <div>
    <div>item type 1</div>
    <pre>{JSON.stringify(props, undefined, 2)}</pre>
  </div>
);
const ItemType2 = (props) => (
  <div>
    <div>item type 2</div>
    <pre>{JSON.stringify(props, undefined, 2)}</pre>
  </div>
);
const ItemHoc = (ItemType) => (props) => (
  <ItemType {...props} />
);
const ListHoc = (Item) => ({ items }) => (
  <ul>
    {items.map((item, index) => (
      <Item key={index} item={item} />
    ))}
  </ul>
);
const Type1List = ListHoc(ItemHoc(ItemType1));
const Type2List = ListHoc(ItemHoc(ItemType2));
const App = () => {
  const items = [1, 2, 3];
  return (
    <div>
      <Type1List items={items} />
      <Type2List items={items} />
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>

Another way is render props:

const ItemType1 = (props) => (
  <div>
    <div>item type 1</div>
    <pre>{JSON.stringify(props, undefined, 2)}</pre>
  </div>
);
const ItemType2 = (props) => (
  <div>
    <div>item type 2</div>
    <pre>{JSON.stringify(props, undefined, 2)}</pre>
  </div>
);
const Item = ({ Render, ...props }) => (
  <Render {...props} />
);
const List = ({ items, ...props }) => (
  <ul>
    {items.map((item, index) => (
      <Item key={index} item={item} {...props} />
    ))}
  </ul>
);
const App = () => {
  const items = [1, 2, 3];
  return (
    <div>
      <List items={items} Render={ItemType1} />
      <List items={items} Render={ItemType2} />
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>

Upvotes: 1

Related Questions