'渲染的钩子比预期少。这可能是由于 HeadlessUI 选项卡意外提前返回语句造成的

问题描述 投票:0回答:1

我正在为一家商店构建一个管理面板,并使用 HeadlessUI 使用

<Tab>
而不是页面来构建它。通过这种方法,选项卡可以正常工作,但后来我尝试为管理员添加角色,因此我使用 allowedTabs:

创建了一个对象
export const allowedTabs = {
   admin: {
      shop: [EShopTab.ORDERS, EShopTab.PRODUCTS, EShopTab.STATISTICS],
      system: [ESystemTab.USERS, ESystemTab.ADMINS, ESystemTab.SETTINGS],
   },
   moderator: {
      shop: [EShopTab.ORDERS, EShopTab.PRODUCTS],
      system: [ESystemTab.SETTINGS],
   },
   accountant: {
      shop: [EShopTab.STATISTICS],
      system: [ESystemTab.SETTINGS],
   },
   analyst: {
      shop: [EShopTab.STATISTICS],
      system: [ESystemTab.SETTINGS],
   },
};

之后我在

tabs
tab.panels
中映射它们:

侧边栏选项卡.tsx:

export const SidebarTabs = () => {
   return (
      <Tab.List className={s.tabs}>
         {Object.keys(tabs).map((key, index) => (
            <SidebarSection sectionKey={key as ESidebarSectionKey} key={index} />
         ))}
      </Tab.List>
   );
};

SidebarSection.tsx:

export const SidebarSection = ({ sectionKey }: TProps) => {
   const { admin } = useSelector((state: RootState) => state.admins);

   const t = useTranslations('Sidebar');

   return (
      <section className={s.sidebarSection}>
         <h1 className={s.title}>{t(sectionKey)}</h1>

         <section className={s.sectionLinks}>
            {allowedTabs &&
               allowedTabs[admin.role] &&
               Object.values(allowedTabs[admin.role][sectionKey]).map((key, index) => (
                  <SidebarTab key={index} label={key} />
               ))}
         </section>
      </section>
   );
};

侧边栏Tab.tsx:

export const SidebarTab = ({ label }: TProps) => {
   const t = useTranslations('Sidebar.Tabs');

   return (
      <Tab as={Fragment}>
         {({ selected }) => (
            <button
               className={cn(s.tab, {
                  [s.active]: selected,
               })}
            >
               {t(label)}
            </button>
         )}
      </Tab>
   );
};

有了选项卡后,我将在另一个组件中渲染它们的面板。由于我不能只渲染 4 个选项卡和 8 个面板(因为第一个选项卡将与错误的面板相关),我还通过 allowedTabs:

进行映射

工作区.tsx:

export const Workspace = () => {
   const { admin } = useSelector((state: RootState) => state.admins);

   return (
      <section className={s.workspace}>
         <Tab.Panels>
            {Object.values(allowedTabs[admin.role].shop).map((key, index) => (
               <Tab.Panel key={index}>{panelByAllowedTab[key]}</Tab.Panel>
            ))}
            {Object.values(allowedTabs[admin.role].system).map((key, index) => (
               <Tab.Panel key={index}>{panelByAllowedTab[key]}</Tab.Panel>
            ))}
         </Tab.Panels>
      </section>
   );
};

渲染面板(考虑到@Konrad评论将所有面板更改为

OrdersTab
,问题仍然存在): tabs.ts:

export const panelByAllowedTab = {
   [EShopTab.ORDERS]: OrdersTab,
   [EShopTab.PRODUCTS]: OrdersTab,
   [EShopTab.STATISTICS]: OrdersTab,
   [ESystemTab.USERS]: OrdersTab,
   [ESystemTab.ADMINS]: OrdersTab,
   [ESystemTab.SETTINGS]: OrdersTab,
};

添加订单选项卡:

export const OrdersTab = () => {
   const dispatch = useDispatch();

   const { orders } = useSelector((state: RootState) => state.orders);
   const { products } = useSelector((state: RootState) => state.products);

   const t = useTranslations('Headers.Orders');

   useEffect(() => {
      dispatch(fetchProducts());
      dispatch(fetchOrders());
   }, []);

   return (
      <TabLayout title="Orders">
         <section className={s.content}>
            <div className={s.header}>
               <p className={s.orderNumber}>{t('orderNumber')}</p>
               <p className={s.orderDate}>{t('date')}</p>
               <p className={s.status}>{t('status')}</p>
               <p className={s.orderTotal}>{t('total')}</p>
               <p className={s.orderDelivery}>{t('delivery')}</p>
               <p className={s.orderAddress}>{t('address')}</p>
               <p className={s.orderPayment}>{t('payment')}</p>
            </div>

            <Button
               onClick={() => {
                  dispatch(
                     createOrder({
                        status: EStatus.NEW,
                        totalprice: 0,
                        createdat: new Date().toISOString(),
                        deliverytype: EDelivery.PICKUP,
                        address: 'St',
                        paymenttype: EPayment.CARD,
                        contactphone: '37721732',
                        paymentstatus: EPaymentStatus.NOT_PAID,
                        filters: [
                           {
                              id: 'd8171941-f4f0-40b7-8642-19bc206d89ec',
                              filters: [
                                 {
                                    label: 'Weight',
                                    value: '1',
                                    extraprice: 0,
                                 },
                                 {
                                    label: 'Color',
                                    value: '#381232',
                                    extraprice: 0,
                                 },
                              ],
                           },
                        ],
                        ordernumber: `${orders.length + 1}`,
                        name: 'Name',
                        products: ['d8171941-f4f0-40b7-8642-19bc206d89ec'].map(id => {
                           return products.find(
                              product => product.id === id,
                           ) as TProductDTO;
                        }),
                        id: crypto.randomUUID() as UUID,
                     }),
                  );
               }}
            >
               Add order
            </Button>

            {orders &&
               orders.length > 0 &&
               orders.map(order => <OrdersCard key={order.id} id={order.id} />)}
         </section>
      </TabLayout>
   );
};

选项卡布局:

export const TabLayout = ({ children, title }: TProps) => {
   return (
      <section className={s.tab}>
         <h1 className={s.title}>{title}</h1>

         <Divider />

         {children}
      </section>
   );
};

当我尝试单击我收到的另一个面板时,会呈现此选项卡和面板:

Unhandled Runtime Error
Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
reactjs typescript next.js tabs headless-ui
1个回答
0
投票

出了什么问题?

这里发生了一些事情:

  1. 我不太确定,但看起来

    Tab.Panel
    可以接受一个函数作为子函数(
    Tabs
    可以肯定阅读更多

  2. 如果不用作
  3. OrdersTab

    ,则将 
    <OrdersTab />

    视为函数

因此

OrdersTab
可能作为函数被调用不同次数,导致
useDispatch
被调用

解决方案

[EShopTab.ORDERS]: OrdersTab
替换为
[EShopTab.ORDERS]: <OrdersTab />

或者像这样定义

Workspace
组件:

export const Workspace = () => {
   const { admin } = useSelector((state: RootState) => state.admins);
   // any name, but must be capitalized
   const Component = panelByAllowedTab[key]

   return (
      <section className={s.workspace}>
         <Tab.Panels>
            {Object.values(allowedTabs[admin.role].shop).map((key, index) => (
               <Tab.Panel key={index}><Component /></Tab.Panel>
            ))}
            {Object.values(allowedTabs[admin.role].system).map((key, index) => (
               <Tab.Panel key={index}><Component /></Tab.Panel>
            ))}
         </Tab.Panels>
      </section>
   );
};
© www.soinside.com 2019 - 2024. All rights reserved.