Home > Enterprise >  Converting to Typescript: Passing mapped props
Converting to Typescript: Passing mapped props

Time:03-08

I am struggling with converting the following React.JS script to TypeScript. Can anyone help? I am trying to make a drop down nav bar in my website.

This is my Header.tsx file:

I am getting a red squiggly line on onClick={closeMobileMenu} - Property 'onClick' does not exist on type 'IntrinsicAttributes & { items: any; }'.

<ul className="navbar-nav">
    {menuItems.map((menu, index) => {
    return (
      <MenuItems 
        items={menu}
        key={index}
        onClick={closeMobileMenu}
      />
    );
  })}
</ul>

This is my Menu.tsx file

I am getting an error on

  1. "items": Binding element 'items' implicitly has an 'any' type
  2. "contains":Property 'contains' does not exist on type 'never'
import React, { useState, useEffect, useRef } from "react";
import {HashLink} from "react-router-hash-link";

import Dropdown from "./Dropdown";
import "./Header.css";

interface MenuItems {
  items: string
  key: number
  onClick: (param: any) => void
} 


const MenuItems = ({ items }) => {
  let ref = useRef();
  const [dropdown, setDropdown] = useState(false);
  const onm ouseEnter = () => {
    window.innerWidth > 960 && setDropdown(true);
  };

  const onm ouseLeave = () => {
    window.innerWidth > 960 && setDropdown(false);
  };

  useEffect(() => {
    const handler = (event: { target: any; }) => {
      if (dropdown && ref.current && !ref.current.contains(event.target)) {
        setDropdown(false);
      }
    };
    document.addEventListener("mousedown", handler);
    document.addEventListener("touchstart", handler);
    return () => {
      // Cleanup the event listener
      document.removeEventListener("mousedown", handler);
      document.removeEventListener("touchstart", handler);
    };
  }, [dropdown]);

  return (
    <li
      className="nav-item"
      ref={ref}
      onm ouseEnter={onMouseEnter}
      onm ouseLeave={onMouseLeave}
      onClick={() => setDropdown(false)}
    >
      {items.submenu ? (
        <>
          <button
            type="button"
            aria-haspopup="menu"
            aria-expanded={dropdown ? "true" : "false"}
          >
            <HashLink smooth to={items.path} className="nav-link">
              {items.title} <i className="fas fa-chevron-down"></i>
            </HashLink>
          </button>
          <Dropdown submenus={items.submenu} dropdown={dropdown} />
        </>
      ) : (
        <HashLink
          smooth to={items.path}
          className="first-level-nav-link"
        >
          {items.title}
        </HashLink>
      )}
    </li>
  );
};

export default MenuItems;

This is my menuItems.tsx file:

export const menuItems = [
  {
    title: "Home",
    path: "/",
    cName: "nav-link",
    submenu: [
      {
        title: "Story",
        path: "/#story",
        cName: "nav-link",
      },
      {
        title: "Map",
        path: "/#map",
        cName: "nav-link",
      }
    ],
  },
  {
    title: "Rewards",
    path: "/",
    cName: "nav-link",
    submenu: [
      {
        title: "competition",
        path: "competition",
        cName: "nav-link",
      },
      {
        title: "prizes",
        path: "prizes",
        cName: "nav-link",
      }
    ],
  },
  {
    title: "Downloads",
    path: "downloads",
    cName: "nav-link",
  }
];

CodePudding user response:

For error #2

TypeScript cannot actually infer how you intend to use this ref without any extra information.

const ref = useRef() // React.MutableRefObject<undefined>

However, useRef can be used as a generic to tell TypeScript how you do intend on using the ref.

const ref = useRef<HTMLLIElement>(null) // React.MutableRefObject<HTMLLIElement>

Only then will TypeScript allow you to access ref.current.contains, because it knows that the contains property exists on a HTMLLIElement node.

CodePudding user response:

Correction #1: For function reference as a parameter you need to define any data type

interface MenuItems {
  items: any,
  key: number,
  onClick: any
} 

Correction #2 : At your MenuItems Component.

const MenuItems = (props: MenuItems ) => {

   //access menu item 
  console.log(props.items.title);

}
  • Related