如何制作一个 React.FC 来渲染一个 Ant Design TabPanes 数组?

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

所以,我正在尝试从 Ant Design 渲染一些选项卡(在这里使用 3.x,就像在我的真实应用程序中一样,但我敢打赌它在 4.x 中完全相同)我需要在我的应用程序的不同部分重用。其中一些选项卡依赖于来自 API 检索的模式的动态数据。根据该数据,选项卡的内容会发生变化,或者某些选项卡甚至不会呈现。

长话短说,我试图用这个片段来简化我的用例。我想要一个 <DynamicTabPanes /> 组件,在给定所需数据的情况下能够处理逻辑,并且 return 需要 TabPanes.

显然,Ant Design 不喜欢这样。如果我像这样使用我的组件:

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

我收到这些错误:

There must be tab property on children of Tabs.

但是,当然,如果我为我的组件提供 keytab 属性,antd将其呈现为简单的 TabPane。参见:

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>

我(有点)使用函数而不是 FC 使其工作,如下所示:

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>

但这似乎不是一个好的解决方案,因为:

  1. React 应该是关于组件的,而不是插值。
  2. <DynamicTabPanes /> 需要使用一些钩子,你不能在非 FC 的函数中使用它们。

关于如何使这项工作有任何想法吗?谢谢!

好的,我做了一些调查,所以我们有以下内容:

  1. Ant-design 使用 'rc-tabs' 中的 TabsTabPane,所有与选项卡渲染相关的逻辑都位于 github src
  2. Tabs 因为包装器组件负责渲染其子组件。 您可以检查 Tabs 组件是否在 parseTabList 函数 src 中迭代它的直接子项。它将您的 DynamicTabPanes 组件检测为单个组件。然后基于直接子项数组的选项卡组件创建导航项 TabNavList 和内容面板列表 TabPanelList(因此选项卡的数量将与直接子项的数量匹配,在您的示例中为两个)。
  3. 所以答案是否定的,你不能用你的自定义组件代替其他一些元素。据我所知,您不能在渲染之前用少数元素替换一个元素。

您可以像下面这样创建您自己的窗格并在那里使用您的挂钩(它不再是每个窗格的单独挂钩):

const CustomPane = (props) => {
  if (!props.active) {
    return <div></div>;
  }
  return (
    <Fragment>
       My custom tab
      <TabPane {...props}>{props.children}</TabPane>
    </Fragment>
  );
};

您可以像下面的代码那样做一些事情,但它只是另一种方法,可以像您一样做同样的事情。您还可以将 staticPanel 替换为静态面板的配置数组,并将其与动态连接,然后进行映射。

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

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

  const staticPanes = [
    <TabPane key="static-1" tab="Static Tab">
      Some static content
    </TabPane>,
  ];

  return (
    <div className="App">
      <Tabs children={DynamicTabPanes(staticPanes, someApiData)} />
    </div>
  );
}

export default App;

希望它能稍微澄清问题的背景。