0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Astroでつくったネスト構造のulリストのスニペット

Posted at

趣味のサイトをSSGのAstroでつくっているときに、ネスト構造のulリストをつくったので、そのスニペットコードです。

Astroは、

JSXに似た式
Astroコンポーネント 🚀 Astroドキュメント | JSXに似た式

なので、この程度のことであれば、JSXを使うらしいReactでも通じる内容かもしれませんが、俺はReactをやったことがないので、通じるかどうかはまったくわかりません。

やりたいこと

ネスト構造のリスト(元データ)をulリスト化(目標のかたち)する。

元データ

    list: [
      {
        name: 'Level1-1',
        slugs: [1, 2, 3],
        children: [
          {
            name: 'Level2-1',
            slugs: [4, 5],
            children: [
              {
                name: 'Level3-1',
                slugs: [6, 7],
              },
              {
                name: 'Level3-2',
                slugs: [8, 9],
              },
            ],
          },
          {
            name: 'Level2-2',
            slugs: [10, 11],
          },
        ],
      },
      {
        name: 'Level1-2',
        slugs: [12, 13],
      },
    ],

目標のかたち

Level1-1
    1
    2
    3
    Level2-1
        4
        5
        Level3-1
            6
            7
        Level3-2
            8
            9
    Level2-2
        10
        11
Level1-2
    12
    13

HTMLコードで言うと以下の通りで、スタイルはulmargin-left: 40px;を当てておくことで、目標のかたちのようにインデントされます。


<ul>
  <li>
    <h1>Level1-1</h1>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>
        <h2>Level2-1</h2>
        <ul>
          <li>4</li>
          <li>5</li>
          <li>
            <h3>Level3-1</h3>
            <ul>
              <li>6</li>
              <li>7</li>
            </ul>
            <h3>Level3-2</h3>
            <ul>
              <li>8</li>
              <li>9</li>
            </ul>
          </li>
        </ul>
        <h2>Level2-1</h2>
        <ul>
          <li>10</li>
          <li>11</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <h1>Level1-2</h1>
    <ul>
      <li>12</li>
      <li>13</li>
    </ul>
  </li>
</ul>

やったこと

index.astro
---
import List from '~/components/List.astro';
import { list } from '~/terms';
---
<List list={list} deep="0"></List>
List.astro
---
import ListElement from '~/components/ListElement.astro';

const {list,deep} = Astro.props;
const nextDeep = Number(deep) + 1;
---
<ul>
  {
  list.map(el => (
    <li><ListElement list={el} deep={nextDeep}></ListElement></li>
  ))
  }
</ul>
ListElement.astro
---
import ListElement from '~/components/ListElement.astro';

const { list, deep } = Astro.props;
const Heading = `h${deep}`;
const nextDeep = Number(deep) + 1;
---
<Heading>{list.name}</Heading>
<ul>
  {list.slugs.map((slug) => <li>{slug}</li>)}
  {
    list.children?.map((el) => (
      <li><ListElement list={el} deep={nextDeep}></ListElement></li>
    ))
  }
</ul>

ひっかかった場所

H1、H2タグ

<h${deep}>みたいなことをしようとして、当然うまくいかなかった。
以下を参照して解決しました。

Astroコンポーネント 🚀 Astroドキュメント | 動的なタグ

deep変数

最初、以下の様に書いて、<h11>となって、なんだこりゃとなりました。

const nextDeep = deep + 1;

そうですね、String型だからですね。

ひっかかっていること

List.astroListElement.astroに同じコードが出現しているので、ここをどうにかできるはずです。
同じコードをコンポーネント化すると、残ったコードが簡素になるので、

<Heading>{list.name}</Heading>
 {list.slugs.map((slug) => <li>{slug}</li>)}

この出力箇所を、データの有無で出し分けすれば、1つのファイルで事足りるかなあと想像しています。

追記(2023/03/12 16:06)
この投稿を書きながら考えていたら、元データの最上層も

{ name, slugs, children}

の形にしてしまえば、解決な気がします。試してないけど。

考え方

このコードにたどりつくための作業は以下の様な感じです。

  1. ネスト構造の繰り返しを意識して、元データを作成する
  2. 元データを元に、手に入れたいHTMLコードを手動で書いてみる
  3. 以上の工程でやりたいことのイメージを固める
  4. 再帰処理を書くため、基本となる構造をHTMLコードから切り出す
  5. 切り出した構造を出力するJSコードを書く
  6. 再帰処理の行き止まり処理を考える(今回の場合はchildrenがなくなること)

こんな感じだったと思うけど、二三度考え直して、コードを仕切り直していたりするので、右往左往しながら、この回答にたどりつきました。

後書き

次に再帰処理を書くときは、もっとスラスラと書けるようになりたいな。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?