Wednesday, 5 May 2021

How to make a React.FC that renders an array of Ant Design TabPanes?

So, I'm trying to render some Tabs from Ant Design (Using 3.x here, as in my real app, but I bet it's exactly the same in 4.x) that I need to reuse in different parts of my App. Some of these tabs depend on dynamic data that comes from an schema retrieved by an API. Depending on that data the content of the tabs changes, or maybe some of the tabs don't even render.

Long story short, I've tried to simplify my use case with this snippet. I want to have a <DynamicTabPanes /> component that given the required data is able to handle the logic and return the needed TabPanes.

Apparently, Ant Design doesn't like this. If I use my component like:

<Tabs>
  <DynamicTabPanes data={someApiData} />
</Tabs>

I get these errors:

There must be tab property on children of Tabs.

But, of course, if I provide a key and a tab property to my component, antd renders it as if it was a simple TabPane. See:

const { Tabs } = antd;
const { TabPane } = Tabs;

const DynamicTabPanes = ({data}) => {
  return data.map((tab, index) => <TabPane tab={tab.label} key={index}>{tab.content}</TabPane>)
}

const someApiData = [{label: "First", content: "First Content"}, {label: "Second", content: "Second Content"}];

ReactDOM.render(
  <Tabs>
    <TabPane key="static-1" tab="Static Tab">Some static content</TabPane>
    <DynamicTabPanes data={someApiData} key="whatever" tab="I don't want this title..."/>
  </Tabs>, document.getElementById('root')
);
<link href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.26.20/antd.css" rel="stylesheet"/>

<div id="root"/>

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment-with-locales.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/3.26.20/antd-with-locales.js" crossorigin="anonymous"></script>

I (kind of) made it work using a function instead of a FC, like this:

const { Tabs } = antd;
const { TabPane } = Tabs;

const renderDynamicTabPanes = (data) => {
  return data.map((tab, index) => <TabPane tab={tab.label} key={index}>{tab.content}</TabPane>)
}

const someApiData = [{label: "First", content: "First Content"}, {label: "Second", content: "Second Content"}];

ReactDOM.render(
  <Tabs>
    <TabPane key="static-1" tab="Static Tab">Some static content</TabPane>
    {renderDynamicTabPanes(someApiData)}
  </Tabs>, document.getElementById('root')
);
<link href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.26.20/antd.css" rel="stylesheet"/>

<div id="root"/>

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment-with-locales.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/3.26.20/antd-with-locales.js" crossorigin="anonymous"></script>

But this doesn't seem like a good solution because:

  1. React should be about Components, not interpolation.
  2. <DynamicTabPanes /> needs to use some hooks and you cannot use them in a function that is not a FC.

Any ideas on how to make this work? Thanks!



from How to make a React.FC that renders an array of Ant Design TabPanes?

No comments:

Post a Comment