75523


ブログを作成していて、記事の背景をループするごとに切り替えていきたくて下記のコード書いたのですが、上手く動作しないです。  
`map()`を使用するのはJSXはレンダーするのに配列にして返す必要があるため、`map()`を二重にしてカラーを適用した要素を返す事で上手くいくと思っていましたが、画面に何も表示されないです。  
教えて頂けないでしょうか、お願いします。

[code]

    import Head from 'next/head'
    import { getPostsData } from '../lib/posts'
    import { getSortedPostsData } from '../lib/posts'
    import { GetStaticProps } from 'next'
    import Link from 'next/link'
    import { Article } from '../types/Article'
    import Layout from '../components/layout'
    
    export default function Home({ 
      sortedPostData,
      colorPattern
    }: {
      sortedPostData: Article[],
      colorPattern: string[]
    }) {
      return (
        <Layout home>
          <div className="">
            <Head>
              <title>Hello</title>
              <link rel="icon" href="/favicon.ico" />
            </Head>
            <ul>
              {sortedPostData.map(({ id, title }) => (
                colorPattern.map((color) => {
                  <li className={color} key={id}>
                    <Link href={`/posts/${id}`}>
                      <a>{title}</a>
                    </Link>
                  </li>
                })
              ))}
            </ul>
          </div>
        </Layout>
      )
    }
    
    export const getStaticProps: GetStaticProps = async () => {
      const allPostsData = await getPostsData() //記事を {[...],[...]...}こんな感じで取得する。
      const sortedPostData = await getSortedPostsData(allPostsData)//上記の記事を書いた日順に並べ換える
      const pattern = ["bg-blue", "bg-blue-light", "bg-gray", "bg-earth-light"]; 
      const max = sortedPostData.length;
      const colorPattern = Array(max).fill("").map((_, i) => pattern[i % 4]);//　記事の数だけ背景色を生成する。
      return {
        props: {
          sortedPostData,
          colorPattern 
        }
      }
    }
    
[/code]


CC BY-SA 4.0
2021-04-26T07:14:51.263
75510
2021-04-26T17:11:56.323
22565
question
javascript reactjs react-jsx next.js
Next.js内のJSXでmapで二重ループしたい。


類似質問: [reactjsでstateの変更ができない](https://ja.stackoverflow.com/q/75455/3068)

>
[code]

>             <ul>
>               {sortedPostData.map(({ id, title }) => (
>                 colorPattern.map((color) => {
>                   <li className={color} key={id}>
>                     <Link href={`/posts/${id}`}>
>                       <a>{title}</a>
>                     </Link>
>                   </li>
>                 })
>               ))}
>             </ul>
>  
[/code]

コメントにも書きましたが、2重ループの内側のループ(`colorPattern.map()`)で値が return されていません。

`.map()`に渡すアロー関数を以下の様に直してください。

[code]

    colorPattern.map((color) => { return (<li ...(中略)... </li>); })
    
[/code]

**コード例**

[code]

    // {} を使用したアロー関数で値を返す場合、明示的に return が必要
    const Home = () => {
      return (
        <ul>
          {[1, 2, 3].map(n => (
            ["a", "b", "c"].map(s => {
              return (
                <li key={`${n}${s}`}>
                  {n}{s}
                </li>
              );
            })
          ))}
        </ul>
      );
    };
    
    ReactDOM.render(
      <React.StrictMode>
        <Home />
      </React.StrictMode>,
      document.getElementById('root')
    );
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
    
    <div id="root"></div>
[/code]

また、アロー関数は return 1文だけの場合、 `{}` と `return` を省略できますので、以下の様に書くこともできます。

[code]

    colorPattern.map((color) => (<li ...(中略)... </li>))
    
[/code]

外側のループの `sortedPostData.map(({ id, title }) => ())`
ではこちらの記法が使われているので、特に修正や変更は不要です。

アロー関数式 - JavaScript | MDN  
<https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions>

* * *

ただ、前記の **コード例** の実行結果からも分かると思いますが、  
この修正を行っても2重ループでReact Elementを生成しているので  
結果的に以下のようなDOMが生成されます。

[code]

    <li className="bg-blue"        key="1"><Link href="/posts/1"><a>title1</a></Link></li>
    <li className="bg-blue-light"  key="1"><Link href="/posts/1"><a>title1</a></Link></li>
    <li className="bg-gray"        key="1"><Link href="/posts/1"><a>title1</a></Link></li>
    <li className="bg-earth-light" key="1"><Link href="/posts/1"><a>title1</a></Link></li>
    
    <li className="bg-blue"        key="2"><Link href="/posts/2"><a>title2</a></Link></li>
    <li className="bg-blue-light"  key="2"><Link href="/posts/2"><a>title2</a></Link></li>
    <li className="bg-gray"        key="2"><Link href="/posts/2"><a>title2</a></Link></li>
    <li className="bg-earth-light" key="2"><Link href="/posts/2"><a>title2</a></Link></li>
    
    <li className="bg-blue"        key="3"><Link href="/posts/3"><a>title3</a></Link></li>
    <li className="bg-blue-light"  key="3"><Link href="/posts/3"><a>title3</a></Link></li>
    <li className="bg-gray"        key="3"><Link href="/posts/3"><a>title3</a></Link></li>
    <li className="bg-earth-light" key="3"><Link href="/posts/3"><a>title3</a></Link></li>
    ...
    
[/code]

これは質問者さんが求めている結果ではないはずです。

(兄弟 node 間で key が重複しているのも問題ですが、それは置いといて)

例えば以下のようなコードにすれば、期待通りの結果が得られるかと思います。

[code]

    const pattern = ["bg-blue", "bg-blue-light", "bg-gray", "bg-earth-light"]; 
    
    function getColorClassFromIndex(index: number): string {
      return pattern[index % pattern.length];
    }
    
    export default function Home({ sortedPostData }: { sortedPostData: Article[] }) {
      return (
        <Layout home>
          <div className="">
            <Head>
              <title>Hello</title>
              <link rel="icon" href="/favicon.ico" />
            </Head>
            <ul>
              {sortedPostData.map(({ id, title }, index) => (
                <li className={getColorClassFromIndex(index)} key={id}>
                  <Link href={`/posts/${id}`}>
                    <a>{title}</a>
                  </Link>
                </li>
              ))}
            </ul>
          </div>
        </Layout>
      )
    }
    
[/code]

整数を4で割った余りが `0, 1, 2, 3, 0, ...` で循環するように、任意の整数nで割った余りは 0 ～ n-1
で循環しますので、これを元に配列から値を取り出せば、配列内の値を循環しながら参照できます。

また、`Array.prototype.map()` に渡す関数の第二引数には、配列の index が渡されますので、いま配列のどの index
の値が渡されているのか知ることができます。

Array.prototype.map() - JavaScript | MDN  
<https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map>

* * *

外部(props)からclassの一覧を渡していますが、li に正しい class を指定しないと意図した通りに表示されないと思いますので、この class
一覧と Home コンポーネントには「強い依存関係」があるため、外部から渡さなくてもいいのかな、と感じました。

もちろん外部から渡すように実装しても良いですが、使用者が使い方を気を付けなければいけないコンポーネントやAPIというのは良くない設計の一例だと思います。

* * *

質問とは無関係ですが、もし `<Link>` のコンポーネントが a タグのDOMを生成するなら、`{title}`を囲んでる a
タグは不要な気がしました。

実際に生成されたDOMが `<a href="/post/id"> <a>title</a> </a>` になっていた場合は修正を検討してみてください。

[code]

    <Link href={`/posts/${id}`}>
      {title}
    </Link>
    
[/code]


CC BY-SA 4.0
2021-04-26T13:58:50.197
75523
2021-04-26T17:11:56.323
2021-04-26T17:11:56.323
3068
3068
75510
answer