[React] Next.js 13の中でPythonを動かす 

人に話すとたまに驚かれますが、じつは[Next.js](https://nextjs.org/)の中でPython環境を動かすことができます。

Pyodideという、WASMのおかげでブラウザ上で使えるPython環境があるのですが、それをReactの中で使いやすくなるhookなどが提供される[react-py](https://github.com/elilambnz/react-py)というnpmパッケージも存在します。

前回の記事と同じように、クライアント側でしか動かない仕組みですが、今回はNext.js 13のクライアント コンポーネントという機能を使ってインポートなどをわかりやすくしようと思いました。

今日の流れはこのようになります：

1. Next.js 13（TypeScript）のテンプレートから始める
2. `react-py`でPythonコードが入力・実行できるコンポーネントを用意
3. Chakraを使って簡単に見た目を良くする

## Next.js 13（TypeScript）のテンプレートから始める

現在、デフォルトでNext.js 13とTypeScriptが導入されます。

```sh
yarn create next-app
```

`npm`や`pnpm`、別のツールを使うことももちろん可能です。この記事では`yarn`で進めていきます。

また、色々と選択することになると思いますが、デフォルトと違った自分の選択肢を共有します：

- Would you like to use Tailwind CSS with this project? › __No__  
  Chakraを使う予定でしたので、Tailwindは不要だと思いました（Python環境の部分に影響ありません）
- Would you like to customize the default import alias? › __Yes__  
  What import alias would you like configured? … __@/*__
  プロジェクト内のコンポーネントがインポートしやすくなります（Python環境の部分に影響ありません）

## `react-py`でPythonコードが入力・実行できるコンポーネントを用意

まずは`react-py`をインストールしましょう。

```sh
yarn add react-py
```

[ドキュメント](https://elilambnz.github.io/react-py/docs/introduction/nextjs-usage)ではもはやNext.js 13のクライアント コンポーネントでの使い方しか紹介されませんが、Next.js 12では`next/dynamic`でインポートできます。既存のプロジェクトでreact-pyをそのまま使いたい場合は[Next.js + p5.jsについて書いた記事](https://qiita.com/alecrem/items/d7f159a39541c72b1712)を参照してください。やり方は同じになります。

今回はあえて最新のNext.js 13で使える、より簡単な方法を紹介したいと思います。先ほどドキュメントへのリンクを貼りましたが、まずはPython環境を使いたいコンポーネントかページの一行目に`'use client'`を付けてクライアント コンポーネントにしましょう。クライアント コンポーネントはサーバー側でのNext.jsによる生成の対象外になるので`next/dynamic`などが不要になります。

### PythonProviderを挿入する

Pythonようにクライアント コンポーネントにしたいページかコンポーネントの一番上をこのようにしましょう：

```tsx
'use client'

import { PythonProvider } from 'react-py'
```

その`PythonProvider`を本ページの出力に加えましょう。`react-py`のhookを使うことになるコンポーネントを囲うようにしましょう：

```tsx

export default function Home() {
  return (
    <PythonProvider>
      <main>
        <Codeblock />
      </main>
    </PythonProvider>
  )
}
```

`Codeblock`はこのチュートリアルの任意コンポーネントになります。

### Codeblockコンポーネント

`react-py`の使い道はいっぱいあると思いますが、この度は[ドキュメント](https://elilambnz.github.io/react-py/docs/introduction/nextjs-usage)の通り`<textarea>`にPythonコードを書いて実行できるコンポーネントにしましょう。

まずは`usePython` hookをインポートしましょう。クライアント コンポーネントにしますので、先ほどの`PythonProvider`と同じくそのままインポートできます：

```tsx
'use client'

import { usePython } from 'react-py'
```

コンポーネントの定義の中で`usePython`を使いましょう：

```tsx
export default function Codeblock() {
  const { runPython, stdout, stderr, isLoading, isRunning } = usePython()

  return (
    <>
      ...
    </>
  )
}
```

取得するのは：
- `runPython: (code: string) => Promise<void>`: Pythonコードを渡すと実行してくれる関数
- `stdout: string`: Pythonの標準出力
- `stderr: string`: Pythonの標準エラー出力
- `isLoading: boolean`: Python環境を読み込み中かどうか（WASM自体がcdn.jsdelivr.netから読み込まれます）
- `isRunning: boolean`: Python環境が処理中かどうか

`usePython`から`isReady`など取得できる定数や関数が他にもありますので、必要に応じて使いたいと思います。

## Chakraを使って簡単に見た目を良くする

最後に、本題ではないですがChakra UIで形にしたいと思います。Chakraを初めてNext.js 13で使ってみましたので勉強になったので、かるく共有したいと思います。

Pyodideと直接関係がないので、違うウェブコンポーネントを使う場合は下記読んでいただかなくても大丈夫だと思います。一応形にしなければならいないと思いましたので、割と簡単な方法で対応してみたまでです。

まずはChakraなどをインストールしましょう。

```sh
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
```

次に、`react-py`に`PythonProvider`があるようにChakraに`ChakraProvider`というのがあります。Chakraコンポーネントを使うところをこれで囲いたいです。

Next.js 13の`layout.tsx`をこのようにしました：

```tsx
import { ChakraProvider } from '@/tools/chakra-client-components'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <head>
        <title>Next.js 13 + Chakra + react-py</title>
      </head>
      <body>
        <ChakraProvider>{children}</ChakraProvider>
      </body>
    </html>
  )
}
```

`ChakraProvider`は、クライアント コンポーネントではなくても使うことができますので、RootLayoutをクライアントコンポーネントにする必要がありません。サーバー側生成とChakraはちゃんと同時に使えます。

あとは[Chakraのドキュメント](https://chakra-ui.com/docs/components/textarea)と完成したコンポーネントのコードを参照してください：

- [page.tsx](https://github.com/alecrem/nextjs-reactpy-sample/blob/main/src/app/page.tsx)
- [Codeblock.tsx](https://github.com/alecrem/nextjs-reactpy-sample/blob/main/src/components/Codeblock.tsx)

一番注意したいところは、HTML5の`textarea`タグの`disabled`プロパティは、Chakraの`Textarea`コンポーネントとなると`isDisabled: boolean`を使わなければならないところです。

## 最後に

Next.jsで外部APIを使わずにPythonをブラウザ上で実行できました。また、Next.js 13の新しいクライアント コンポーネントを使ってブラウザ側でしか使えないコンポーネントやhookをスムーズにインポートできました。

任意のPythonパッケージのインストールなどPyodideが充実していますので、したいことが意外と可能だったりしますのでユーザーの生データを外部APIに送りたくない場面では積極的に考えたい選択肢だと思います。

[サンプルリポ](https://github.com/alecrem/nextjs-reactpy-sample/)もよかったらご参照ください。そしてご不明点、ご要望などありましたらコメントやツイッターでご連絡いただければ嬉しいです。