Open Liberty＋ReactでWEBアプリを作ってみる

Open Libertyの公式ページにある以下サンプルを使用して、Webアプリを作成してみましょう。
https://openliberty.io/guides/rest-client-reactjs.html

# Open Libertyってなに？
- 高速で効率的なクラウドネイティブJavaマイクロサービスを構築するための軽量なオープンフレームワークです。
- 要するにJava製のアプリケーションサーバーです。
- Open Liberty 公式ページ：https://openliberty.io/

# 環境
- Windows11 + WSL2(Ubuntu 20.04.5)
- Mavenインストール済み
- ブラウザはEdge
- node v16.13.2
- openjdk 17.0.8


# リポジトリのクローンと完成品の確認

リポジトリをクローンし、ディレクトリを移動します。

```
git clone https://github.com/openliberty/guide-rest-client-reactjs.git
cd guide-rest-client-reactjs
```

中にはfinishとstartという2つのディレクトリが含まれており、finishの方にサンプルの完成品が格納されています。
finishディレクトリに移動し、完成品を確認してみましょう。

```
cd finish
mvn liberty:run
```

ブラウザで`http://localhost:9080/`を開くと、以下のような画面を見ることができます。

![react-table.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327995/00288984-7c68-74ea-f1f1-fd119b9c881c.png)



# 未完成のWebアプリを実行してみる

startディレクトリに移動し、`mvn liberty:dev`コマンドを実行してみます。今回はブラウザで`http://localhost:9080/`を開いても真っ白なページしか表示されません。`http://localhost:9080/artists`にアクセスすると、jsonが返却されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327995/283f9de8-7eb3-b94b-7930-7cfc6e68338b.png)

`http://localhost:9080/artists`からjsonデータを受け取り、Reactで表示できるようにする必要がありそうです。


# React部分の実装

まず、`index.js`を作成します。このファイルはreactアプリのエントリーポイントになります。

```react:src/main/frontend/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './Styles/index.css';
import App from './Components/App';

ReactDOM.render(<App />, document.getElementById('root'));
```

さらに、以下のように`App.js`ファイルを作成します。

```react:src/main/frontend/src/Components/App.js
import React from 'react';
import {ArtistTable} from './ArtistTable';

function App() {
  return (
      <ArtistTable/>
  );
}

export default App;
```

`ArtistTable.js`を作成します。このコンポーネントでテーブルを作成します。

```react:src/main/frontend/src/Components/ArtistTable.js
import React, { useEffect, useMemo, useState } from 'react';
import { useTable, usePagination, useSortBy } from 'react-table';
import '../Styles/table.css'

export function ArtistTable() {

  const [posts, setPosts] = useState([]);


  const data = useMemo(() => [...posts], [posts]);

  const columns = useMemo(() => [{
    Header: 'Artist Info',
    columns: [
      {
        Header: 'Artist ID',
        accessor: 'id'
      },
      {
        Header: 'Artist Name',
        accessor: 'name'
      },
      {
        Header: 'Genres',
        accessor: 'genres',
      }
    ]
  },
  {
    Header: 'Albums',
    columns: [
      {
        Header: 'Number of Tracks',
        accessor: 'ntracks',
      },
      {
        Header: 'Title',
        accessor: 'title',
      }
    ]
  }
  ], []
  );

  const tableInstance = useTable(
    {
      columns,
      data,
      initialState: { pageIndex: 0, pageSize: 4 }
    },
    useSortBy,
    usePagination
  )

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page,
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    state: { pageIndex, pageSize }
  } = tableInstance;


  return (
    <>
      <h2>Artist Web Service</h2>
      <table {...getTableProps()}>
        <thead>
          {headerGroups.map(headerGroup => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map((column) => (
                <th {...column.getHeaderProps(column.getSortByToggleProps())}>
                  {column.render('Header')}
                  <span>
                    {column.isSorted
                      ? column.isSortedDesc
                        ? ' 🔽'
                        : ' 🔼'
                      : ''}
                  </span>
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {page.map((row, i) => {
            prepareRow(row)
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map(cell => {
                  return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
                })}
              </tr>
            )
          })}
        </tbody>
      </table>
      <div className="pagination">
        <button onClick={() => previousPage()} disabled={!canPreviousPage}>
          {'Previous'}
        </button>{' '}
        <div class="page-info">
          <span>
            Page{' '}
            <strong>
              {pageIndex + 1} of {pageOptions.length}
            </strong>{' '}
          </span>
          <span>
            | Go to page:{' '}
            <input
              type="number"
              defaultValue={pageIndex + 1}
              onChange={e => {
                const page = e.target.value ? Number(e.target.value) - 1 : 0
                gotoPage(page)
              }}
              style={{ width: '100px' }}
            />
          </span>{' '}
          <select
            value={pageSize}
            onChange={e => {
              setPageSize(Number(e.target.value))
            }}
          >
            {[4, 5, 6, 9].map(pageSize => (
              <option key={pageSize} value={pageSize}>
                Show {pageSize}
              </option>
            ))}
          </select>
        </div>
        <button onClick={() => nextPage()} disabled={!canNextPage}>
          {'Next'}
        </button>{' '}
      </div>
    </>
  );
}
```

上記のままでは、jsonデータを受け取ることができません。そこで、以下のように`axios`のインポートと`GetArtistsInfo`関数を追加します。`GetArtistsInfo`関数では、axiosで`http://localhost:9080/artists`からデータを受け取っています。

```react:src/main/frontend/src/Components/ArtistTable.js

import React, { useEffect, useMemo, useState } from 'react';
import axios from 'axios';
import { useTable, usePagination, useSortBy } from 'react-table';
import '../Styles/table.css'

export function ArtistTable() {

  const [posts, setPosts] = useState([]);

  const GetArtistsInfo = async () => {
    const response = await axios.get('http://localhost:9080/artists')
      .then(response => {
        const artists = response.data;
        for (const artist of artists) {
          const { albums, ...rest } = artist;
          for (const album of albums) {
            setPosts([...posts, { ...rest, ...album }]);
            posts.push({ ...rest, ...album });
          }
        };
      }).catch(error => { console.log(error); });
  };
  const data = useMemo(() => [...posts], [posts]);

  const columns = useMemo(() => [{
    Header: 'Artist Info',
    columns: [
      {
        Header: 'Artist ID',
        accessor: 'id'
      },

// 以下省略・・・・・
```

以上がすべて終わったら、startディレクトリに戻り、以下コマンドを実行します。このコマンドでビルドが走ります。

```
mvn process-resources
```

 http://localhost:9080 にアクセスしてみましょう。以下のような画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327995/97a5dabc-56d0-428d-338f-3191b5b80b87.png)

# まとめ
Open LibertyとReactを利用して、簡単なWebサービスを実装できました。みなさんも試してみてください！