Nextjs(React)でオブジェクトのプロパティをmapでループ中にClassNameに動的に代入
Nextjs(React)でオブジェクトのプロパティをmapでループ中にClassNameに動的に代入
オブジェクトのプロパティiconをclassNameでかつCSS Moduleの記法でclassName={Styles.item.icon}のように表現したくて苦戦したが、成功したのでメモ。
コードサンプル
サイドメニューにメーカー名、リンク先URLと、カテゴリー名のところにアイコンを付けたく、こんなオブジェクト(配列)を用意した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
@メニューファイル.tsx type Categoy = { name: string, icon: string, childmenu: { url: string, brand: string }[] }[] const sideMenu: Categoy = [ { name: "バッグ", icon: "icon_bag", childmenu: [ { url: "***", brand: "すべて見る", }, { url: "***", brand: "メーカーA", }, { url: "***", brand: "メーカーB", }, { url: "***", brand: "メーカーC", }, ], }, { name: "財布", icon: "icon_wallet", childmenu: [ { url: "", brand: "すべて見る" }, { url: "***", brand: "メーカーD", }, { url: "***", brand: "メーカーE", }, { url: "***", brand: "メーカーF", }, ] } ]; export default sideMenu; |
これを本体で展開する。1階層目のプロパティiconをclassNameでかつCSS Moduleの記法でclassName={Styles.item.icon}のように表現したい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
@本体ファイル.tsx "use client"; import { JSXElementConstructor, useState, ReactElement } from "react"; import Link from "next/link"; import sideMenu from "メニューファイル"; import Styles from "SCSSファイル"; export default function SideMenu(): ReactElement { const [openIndexes, setOpenIndexes] = useState<number[]>([]); const handleToggle: (index: number) => void = (index: number): void => { const isIndexContain: number = openIndexes.indexOf(index); const newIndexes: number[] = [...openIndexes]; if(isIndexContain === -1) { if(openIndexes.length !== 0) { newIndexes.splice(0); } newIndexes.push(index); } else { newIndexes.splice(isIndexContain, 1); } setOpenIndexes(newIndexes); } const menu: ReactElement<any | string | JSXElementConstructor<any>>[] = sideMenu.map((item: {name: string, icon: string, childmenu: { url: string; brand: string }[]}, index: number): ReactElement => { // 1階層目をmapで展開 return ( <div className={Styles.category} key={index}> <span onClick={(): void => handleToggle(index)} className={`${Styles.icon} ${openIndexes.includes(index) ? Styles.open : ''} ${Styles[item.icon]}`}>{item.name}</span> <ul> {/* 2階層目をmapで展開 */} {item.childmenu.map((children: {url: string, brand: string}, index: number): ReactElement => { return ( <li key={index}><Link href={children.url}">{children.brand}</Link></li> ); })} </ul> </div> ); } ); return ( <> {menu} </> ); } |
結論。${Styles[item.icon]}が正解でした。
失敗
1 |
<span onClick={(): void => handleToggle(index)} className={`${Styles.icon} ${openIndexes.includes(index) ? Styles.open : ''} ここ! ${Styles.item.icon}`}>{item.name}</span> |
失敗
1 |
<span onClick={(): void => handleToggle(index)} className={`${Styles.icon} ${openIndexes.includes(index) ? Styles.open : ''} ここ! ${Styles.${item.icon}}`}>{item.name}</span> |
失敗
1 |
<span onClick={(): void => handleToggle(index)} className={`${Styles.icon} ${openIndexes.includes(index) ? Styles.open : ''} ここ! Styles + ${item.icon}`}>{item.name}</span> |
成功
1 |
<span onClick={(): void => handleToggle(index)} className={`${Styles.icon} ${openIndexes.includes(index) ? Styles.open : ''} ここ! ${Styles[item.icon]}`}>{item.name}</span> |
コメント
コメントはありません。