入力された漢字が小学何年生で習うものなのかを判定するWEBアプリ　ft. Next.js + vercel

ちょっとしたWEBアプリを作ったので、ポートフォリオとして使えるようにこっちでも仕組みをメモしておきます。

【作ったもの】
入力された漢字が小学何年生で習うものなのかを判定するWEBアプリ。
URL：https://portfolio-pink-omega-28.vercel.app/
※文科省の学年別漢字配当表を使用して開発を行いました。
URL：https://www.mext.go.jp/a_menu/shotou/new-cs/youryou/syo/koku/001.htm 


【開発環境】
```diff_javascript
node:v18.15.0
npm:v9.6.4
tailwindcss:v3.3.1
//cssにはtailwindを用いました。
```


【フォルダツリー】
```diff_javascript
pages
│  index.tsx
│  _app.tsx
│  _document.tsx
├─api
│      ElementaryAPI.tsx
└─src
    └─DataBase
            Elementary1.json
            Elementary2.json
            Elementary3.json
            Elementary4.json
            Elementary5.json
            Elementary6.json
```
開発に関与したファイルはpagesフォルダ配下にすべてまとめています。


【開発手順】
①データベースを用意する。

②index.tsxにフロントの記述を行う。
※フォームに入力された値をstateで保持し、別の関数に渡す準備をする。

③ElementaryAPI.tsxに、データベースの情報を配列にまとめ、入力された漢字のそれぞれがどの学年のデータベースにあるかを探すコードを書いて、配列の形で結果を出力する関数をつくる。
※この関数を関数コンポーネントとして表示させるようにしても良かったと思うのですが、表示とロジックは切り分けた方がよいかと考えたので、関数としての役割に専念させています。

④フロント側でElementaryAPI.tsxの関数に入力情報をわたし、帰ってきた出力情報をJSX記法で表示する。

全体としては、データベースとAPIとフロントの三つで構成されています。本当だったらAPIにはステータスを持たせて、ステータスコードの如何で処理を分けるようなちゃんとしたAPIにしたかったのですが、いかんせん未だ学習途上なのでうまくいきませんでした。APIに関わる所が現状一番の課題です。
以下には、各ファイルに記述した実際のコードを示していきます。


【コードの様子】
Ⅰデータベース。

```diff_javascript
{
    "grade":"小学一年生",   
    "learnedWords":["一", "右", "雨", "円", "王", "音", "下", "火", "花", "貝", "学", "気", "九", "休", "玉", "金", "空", "月", "犬", "見", "五", "口", "校", "左", "三", "山", "子", "四", "糸", "字", "耳", "七", "車", "手", "十", "出", "女", "小", "上", "森", "人", "水", "正", "生", "青", "夕", "石", "赤", "千", "川", "先", "早", "草", "足", "村", "大", "男", "竹", "中", "虫", "町", "天", "田", "土", "二", "日", "入", "年", "白", "八", "百", "文", "木", "本", "名", "目", "立", "力", "林", "六"]
}

```
json形式で各学年で習う漢字をまとめました。


ⅡAPI
```diff_javascript
import El1 from "../src/DataBase/Elementary1.json";
import El2 from "../src/DataBase/Elementary2.json";
import El3 from "../src/DataBase/Elementary3.json";
import El4 from "../src/DataBase/Elementary4.json";
import El5 from "../src/DataBase/Elementary5.json";
import El6 from "../src/DataBase/Elementary6.json";

export const searchGrade=(word:string[]):string[]=>{
  const DB=[El1,El2,El3,El4,El5,El6]; //１
  const res=[] //２
  for (let i=0,length=word.length;i<length;i++){
    let validator:number=0
    for(let index=0,len=DB.length ;index<len;index++){
      DB[index].learnedWords.includes(word[i]) && res.push(`"${word[i]}"は${DB[index].grade}で習います`) & validator++
    }
    validator || res.push(`"${word[i]}"は小学生で習いません`)
  }
  return res
}
```

１：DBという変数にデータベースの情報を配列の形でまとめました。中身は以下のようになっているはずです。
```diff_javascript
const DB=[{grade:"小学一年生",learnedWords:["一","右",...]},{grade:"小学二年生",learnedWords:[...]},...];
```

２：反復処理の中では、それぞれの漢字につきどの学年で習うかの情報をresという空配列の中に入れています。
index番目のDBのプロパティー"learnedWords"にアクセスしたとき、その配列の中にi番目のword（入力された漢字）が含まれていれば、それを習う学年を配列の中にいれます。もし、どの学年でも習わなければ、validatorが0のままのはずなので、小学校では習わない（厳密に言えば文科省の学年別漢字配当表にはない）という情報を配列に入れます。
※宣言的なコードにしたいなら、Arrayオブジェクトに対してはmapメソッドを用いるのが望ましいのでしょうが、mapがネストされることになり、自分はそっちのほうが違和感があったので、反復処理のネストという形をとりました。


Ⅲフロント
```diff_javascript
import { searchGrade } from './api/ElementaryAPI'
import { ChangeEvent, FormEvent, useState } from 'react'

export default function Home() {
  const [texts,setTexts]=useState<string>("")
  const [result,setResult]=useState<string[]>([])

  const onSubmit=(e:FormEvent<HTMLFormElement>)=>{
    e.preventDefault()
    const wordList=texts.replace(/\s+/ug,"/").split("/"); //１
    const res=searchGrade(wordList)
    setResult(res);
    setTexts("");
  }
  
  return (
    <main className="flex min-h-screen flex-col items-center justify-around p-24">
      <div className='text-center'>
        <p>入力した漢字が小学生で習うものかどうかを判別します。複数入力ができます。</p>
        <p>スペースで区切って入力してください。</p>
      </div>
      <form onSubmit={(e)=>onSubmit(e)}> //２
        <input type="text" className='p-2 box-border m-2' name="name" value={texts} onChange={(e:ChangeEvent<HTMLInputElement>)=>setTexts(e.target.value)} />
        <button type='submit' className='bg-gray-500 hover:bg-gray-400 text-white rounded px-4 py-2 m-2'>送信</button>
      </form>
      <div className='container border rounded p-2 text-center'>
        <ul >
          {result.length ? (
            <p>以下に結果を表示します</p>):(
            <p>未入力</p> //３
          )}
          <br/>
          {result.map((res,index)=>(
            <li key={index}>
              <p>{res}</p>
            </li>
          )
          )}        
        </ul>
      </div>
    </main>
  )
}
```

１：半角、全角スペースに関わらず、且つスペースが続いても処理できるように、正規表現を用いて字の区切り目をスラッシュに差し替え、スラッシュを基準に入力文字を切って配列にしています。

２：form要素のonSubmit属性によってインプット要素に入力された情報の提出を管理しています。form要素はbutton要素のsubmitと紐づけられることで、タグ囲みされてある各input要素のvalueを送信することができるようになります。コントロールがformに紐づいていると言ったら分かりやすいでしょうか。

３：結果の配列には何かしらの値が入って帰ってくるはず、つまり”result”の長さが０ということはまだ初期値であるということなので、三項演算子の評価がfalsyなときはこのように「未入力」とだけ出力するようにしています。

【おわりに｜なぜこのアプリを作ろうと思ったか】
小説で小学生が登場する場面があるのですが、そのとき、その学年までには習っていないであろう語彙を使うことがあります。中学受験を目指しているならまだしも、そいういう特殊な設定もないまま結構な漢字を使うキャラを見ていると、なんだかなーって思っていました。
ので、気になった漢字を小学校で習うのかどうか（文科省公認のものだけですが）を調べるアプリを作ってみた次第です。
よく、Next.jsやReact.jsではTodoアプリを作ろう！というデモこそあれ、なかなか他には手軽なWebアプリの制作例はお目にかかれない気がします。あってもJsonplaceholderを使うくらいでしょうか。
拙いコーディングで恐縮ですが、このコードがNext.js学習者の方のちょっとした練習台になれれば幸いです。