React-Tabs分组件实现TabPanel时Tab与TabPanel数量不匹配报错
不需要把react-tabs的所有组成单元都声明在同一个组件内,你遇到的警告是当前拆分写法阻隔了react-tabs的内部元素识别逻辑导致的。
react-tabs的内部校验、Tab和TabPanel的关联逻辑,都是通过直接遍历Tabs组件的一级子节点实现的:它会识别一级子节点里类型为Tab、TabList、TabPanel的元素做数量统计。
你之前的写法是把TabList和下属Tab放在父组件,把TabPanel包裹在Tables、Graphs自定义子组件中,作为Tabs的一级子节点传入。这时候Tabs遍历一级子节点时,只能识别到TabList下的2个Tab,而Tables/Graphs返回的TabPanel因为被包裹在自定义组件节点下,不会被识别为Tabs的直接TabPanel子元素,所以统计到0个TabPanel,抛出数量不匹配的警告。
以下两种方案都可以满足你拆分组件、独立维护子组件状态的需求,不会出现警告:
方案1:保留公共TabList,透传TabPanel
如果Tab栏是固定不随mode切换的,可以在父组件保留公共TabList,子组件用Fragment包裹自身的TabPanel返回,父组件提取子组件的children作为Tabs的直接子节点即可,示例代码:
// 父组件AppTabs import { Tabs, TabList, Tab } from 'react-tabs' import Tables from './Tables' import Graphs from './Graphs' const AppTabs = () => { const [mode, setMode] = useState('table') const currentPanels = mode === 'table' ? <Tables /> : <Graphs /> return ( <Tabs> <TabList> <Tab>第一项</Tab> <Tab>第二项</Tab> </TabList> {/* 直接传入TabPanel元素,不被自定义组件阻隔 */} {currentPanels.props.children} </Tabs> ) } // 子组件Tables(独立维护自身状态) import { TabPanel } from 'react-tabs' const Tables = () => { // 表格相关状态全部在这里维护,不需要上浮到父组件 const [tableData1, setTableData1] = useState([]) const [tableData2, setTableData2] = useState([]) return ( <> <TabPanel>/* 第一个表格的内容 */</TabPanel> <TabPanel>/* 第二个表格的内容 */</TabPanel> </> ) } // 子组件Graphs和Tables逻辑一致,独立维护图表相关状态即可
方案2:按mode拆分完整Tab模块
如果切换mode时Tab文案、数量也会变化,可以直接把TabList+Tab+TabPanel全部放到对应子组件中,父组件只保留Tabs外壳,子组件用Fragment包裹所有react-tabs元素返回即可,示例代码:
// 父组件AppTabs import { Tabs } from 'react-tabs' import Tables from './Tables' import Graphs from './Graphs' const AppTabs = () => { const [mode, setMode] = useState('table') return ( <Tabs> {mode === 'table' ? <Tables /> : <Graphs />} </Tabs> ) } // 子组件Tables import { TabList, Tab, TabPanel } from 'react-tabs' const Tables = () => { // 独立维护表格相关所有状态、Tab逻辑 const [tableData, setTableData] = useState([]) return ( <> <TabList> <Tab>表格1</Tab> <Tab>表格2</Tab> </TabList> <TabPanel>表格1内容</TabPanel> <TabPanel>表格2内容</TabPanel> </> ) } // 子组件Graphs同理,独立维护自身Tab和面板、状态逻辑
注意:不要用自定义组件直接包裹
Tab/TabPanel作为Tabs的一级子节点,用Fragment(<>...</>)包裹不会生成额外的组件节点,不会干扰react-tabs的元素识别。
内容的提问来源于stack exchange,提问作者elita185

