Creating a dynamic navigation menu with antd in TypeScript a breeze

I am currently working on creating a dynamic menu component in antd with typescript.

Although I found a solution using javascript here React Antd dynamic dropdown menu, it seems that when implementing it in typescript, the only available attributes for item are className, key, style.

Does anyone have suggestions on how to proceed with this in typescript?

Below is the code snippet where the build fails at icon={item?.icon} and {item?.label}.

import type { MenuProps } from 'antd';

function CustomAppBarMenu() {
    const [menuItems, setMenuItems] = useState<MenuProps['items']>([]);

    const main_window_menu_items: MenuProps['items'] = [
        {
            key: 'setting',
            label: 'Setting',
            icon: <IconSetting/>,
        },
        {
            key: 'upgrade',
            label: 'Upgrade',
            icon: <IconUpgrade />,
            disabled: true,
        },
        {
            key: 'help',
            label: 'Help',
            icon: <IconHelp />,
        },
        {
            key: 'about',
            label: 'About',
            icon: <IconAbout />,
        },
    ];

    const edit_menu_items: MenuProps['items'] = [
        {
            key: 'import',
            label: 'Import',
            icon: <IconImport/>,
        },
        {
            key: 'export',
            label: 'Export',
            icon: <IconExport />,
        },
    ];

    const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
        console.log(`Click on item ${key}`);
    }

    useEffect(() => {
        if (appWindow.label === 'main') {
            setMenuItems(main_window_menu_items);
        } else if (appWindow.label === 'edit') {
            setMenuItems(edit_menu_items);
        }
    })

    return (
        <Menu onClick={handleMenuClick}>
            {
                menuItems?.map((item) => {
                    return (
                        <Menu.Item key={item?.key} icon={item?.icon}>
                            {item?.label}
                        </Menu.Item>
                    )
                })
            }
        </Menu>
    )
}

Answer №1

ItemType is a union type comprising

MenuItemType | SubMenuType | MenuItemGroupType | MenuDividerType | null

out of these types, only 2 have the icon property and not all of them possess the label property

# ./node_modules/antd/es/menu/hooks/useItems.d.ts
export type ItemType = MenuItemType | SubMenuType | MenuItemGroupType | MenuDividerType | null;

Hence, you can either:

  1. perform type narrowing within the map function (if possible)
  2. or utilize precise menu item types that include both icon & label properties

Code #2 (specific menu item types)

import React, { useState, useEffect } from 'react';
import { Menu } from 'antd';

import type { ItemType, MenuItemType, SubMenuType } from "antd/es/menu/hooks/useItems";

const IconSample = () => <span>tada!</span>;

const appWindow = {
  label: 'main'
};

type MyItemType = MenuItemType | SubMenuType;

const mainWindowMenuItems: MyItemType[] = [
  {
    key: 'setting',
    label: 'Setting',
    icon: <IconSample />,
  },
  {
    key: 'upgrade',
    label: 'Upgrade',
    icon: <IconSample />,
    disabled: true,
  },
  {
    key: 'help',
    label: 'Help',
    icon: <IconSample />,
  },
  {
    key: 'about',
    label: 'About',
    icon: <IconSample />,
  }
];

const editMenuItems: MyItemType[] = [
  {
    key: 'import',
    label: 'Import',
    icon: <IconSample />,
  },
  {
    key: 'export',
    label: 'Export',
    icon: <IconSample />,
  },
];

export function CustomAppBarMenu() {
  let menuItems: MyItemType[] = [];

  if (appWindow.label === 'main') {
    menuItems = mainWindowMenuItems;
  } else if (appWindow.label === 'edit') {
    menuItems = editMenuItems;
  }

  return (
    <Menu>
      {
        menuItems?.map((item) => {
          return (
            <Menu.Item key={item?.key} icon={item?.icon}>
              {item?.label}
            </Menu.Item>
          )
        })
      }
    </Menu>
  )
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When webpack is executed, everything runs smoothly; however, WebStorm raises an

An untouched fork of the Angular 2 webpack boilerplate on Github called Angular 2 webpack boilerplate is causing WebStorm to report an error. The error message pertains to the App in the line import {App} from './components/index'; found in the f ...

Troubleshooting issues with setting errors on a FormGroup instance in Angular Reactive Forms are proving to be more challenging

Currently I am working with Angular 4.4.3 reactive forms to create custom validation for a group of controls within a form. As per the documentation on AbstractControl.setErrors, this method updates the errors property of the AbstractControl that it's ...

What is the process for importing a file with an .mts extension in a CJS-first project?

Here's a snippet from a fetchin.mts file: import type { RequestInfo, RequestInit, Response } from "node-fetch"; const importDynamic = new Function("modulePath", "return import(modulePath);") export async function fetch(u ...

Error TS2339: The type 'Element' does not have a property named 'style'

Below is the code snippet to change the style of elements: const test = Array.from(document.getElementsByClassName('mat-form-field-infix')); test.forEach((element) => { element.outerHTML = '<div class="good-day-today" style="width ...

Error in TypeScript - Anticipated 1-2 arguments, received either none or multiple. Code Issue TS2556

While working in JavaScript, I had no issues with this code snippet. However, when I attempted to implement it in a TypeScript Project, an error surfaced. The problem seems to revolve around the fetch(...args) function call. const fetcher = (...args) =&g ...

Struggling with TypeScript errors due to React.HTMLProps for HTMLAnchorElement

When trying to extend a React component with React.HTMLProps without explicitly defining onClick in the attribute list, ESLint complains when passing onClick as an attribute while using the component. Here's an example of the code: The React componen ...

How can I utilize Pinia and TypeScript to set the State using an Action?

I have a Pinia + TypeScript store named user.ts with the following structure: import { User } from 'firebase/auth'; import { defineStore } from 'pinia'; export const useUserStore = defineStore('user', { state: () => ( ...

Interacting with icons using TouchableOpacity and onPress functionality

I am attempting to implement onPress functionality for icons using TouchableOpacity. However, when I click on the icon, nothing happens and there are no console logs displayed. I have also tried enclosing the icon within an additional View, but that appro ...

Is the type narrowed by type guards only when true is returned?

It was my understanding that a type guard handling multiple types instanceOfA(arg: A | B | C): arg is A, would narrow the type to either A (if the guard returns true) or B | C (if it returns false) However, in the case of instanceOfB below, when returning ...

Leveraging the power of TypeScript and Firebase with async/await for executing multiple

Currently, I am reading through user records in a file line by line. Each line represents a user record that I create if it doesn't already exist. It's possible for the same user record to be spread across multiple lines, so when I detect that it ...

Creating Apache Arrow vectors in TypeScript for writing data to a Table

Currently, I am in the process of creating a method that is designed to take a column of data, referred to as data: any[], and then pack it into an Arrow-typed Array Buffer for insertion into an Arrow table. To illustrate with an example, if we consider T ...

Reactjs - The createStore function is unable to identify the reducer when TypeScript is being used

I recently delved into the world of Reactjs and Typescript and attempted to create a todo item project. However, I encountered an error in VSCode when trying to call createStore that I am struggling to resolve. Any assistance on this matter would be great ...

Is there a way to incorporate a personalized marker onto a map with react-map-gl?

I am looking to enhance my map with a custom marker. Currently, I am using the react-map-gl library along with components Layer and Source. Here is a snippet of my code: import pin from "../images/pin.svg"; . . . . const geojson: GeoJSON.Feature<GeoJSO ...

Guide on merging paths in distinct modules within an Angular project

I am looking to merge two sets of routes from different modules in my application. The first module is located at: ./app-routing.module.ts import {NgModule} from '@angular/core'; import {Routes, RouterModule} from '@angular/router'; i ...

Understanding Different Symbols in TypeScript

Can you explain the purpose of symbols in TypeScript to me? As someone familiar with Java, it seems a bit unnecessary to use them alongside an interface declaration. What is the reason for setting symbols in TypeScript? For example, consider the followin ...

The identifier 'before' could not be located

While working with jest and typescript, I encountered an issue when using "before" calls: Cannot find name 'before'.ts(2304) I made sure to have @types/jest installed already. Update: It appears that jest does not have a "before" function - it ...

Content method in Esri Maps PopupTemplate is executed only once for each feature

I am integrating Esri maps into an Angular app and utilizing Angular components to render popups by passing data from the PopupTemplate's content method: layer.popupTemplate = new PopupTemplate({ content: (feature: { graphic: __esri.Graphic }) => ...

Can you explain the significance of using square brackets in the typescript enum declaration?

While reviewing a typescript file within an Angular ngrx project titled collection.ts, I came across the declaration of enum constants. import { Action } from '@ngrx/store'; import { Book } from '../models/book'; export enum Collecti ...

Repeating promises resolutions yields stagnant outcomes

Within my Angular project, I am working with two distinct components. parent.component.ts mypromise = this.httpClient.get<any>('http://localhost').toPromise() parent.component.html <app-children #child [promise]="mypromise"></a ...

What are the steps to create a dynamic navigation menu in Angular 2?

I have successfully implemented this design using vanilla CSS and JS, but I am encountering challenges when trying to replicate it in Angular 2. Setting aside routing concerns, here is the current state of my component: navbar.component.ts import { Comp ...