【Next.js】【Typescript】計算履歴を記録する電卓アプリを作成してみる

自分用のメモ

環境の準備

実行環境

  • VS code:v1.62.3
  • Node.js:v14.17.3
  • React:v17.0.2
  • Typescript:v4.1.6

プロジェクトの作成(Typescriptで作成)

Next Create Appで作成します。

pagesフォルダ配下のソース

  • 9~16行目の内容が、Layoutコンポーネントの中で「props.children」で参照できます。
import type { NextPage } from 'next'
import Calc from './components/Calc'
import Layout from './components/layout'

const Home: NextPage = () => {
  return (
    <div>
      <Layout title='Calculator'>
        <div className='text-center'>
          <Calc />
        </div>
      </Layout>
    </div>
  )
}

export default Home

pages/api配下のソース

  • 内部のAPI経由でjsonDataを取得する処理です。
import type { NextApiRequest, NextApiResponse } from 'next'
import jsonData from '../components/jsonData'

type Data = {
  data: any
}
const handler = (req: NextApiRequest, res: NextApiResponse<Data>) => {
  res.status(200).json(jsonData)
}

export default handler

pages/components配下のソース

ページを構成するレイアウト

  • Layoutコンポーネントについては、こちらの記事を参考にしてください。

ボタンとアクションの記述

  • 各ボタンの処理を記述しています。
export default {
  'data': {
    'tax': {
      'caption': '入力した金額から消費税(10%)価格を計算します。',
      'function': '(...param) => {return Math.floor(param[0] * 1.1)}'
    },
    'tax2': {
      'caption': '入力した金額から軽減税率(8%)価格を計算します。',
      'function': '(...param) => {return Math.floor(param[0] * 1.08)}'
    },
    'total': {
      'caption': '10,20,30...というようにカンマで区切った数字の合計を計算します。',
      'function': `(...param) => {
        let re = 0
        for (let i = 0; i < param[0].length; i++) {
          re += parseInt(param[0][i])
        }
        return re
      }`
    },
    'factorial': {
      'caption': 'ゼロから入力値までの合計を計算します。',
      'function': `(...param) => {
        console.log(param)
        let re = 0
        for (let i = 0; i <= param[0]; i++) {
          re += i
        }
        return re
      }`
    }
  }
}

  • 11行目のuseEffectでjsonDataを取得しちえます。
  • 53~58行目で、jsonDataに記載されたボタンとアクションが作成されます。
  • 68行目は、履歴クリアボタンです。
import { useState, useEffect } from "react"

const Calc = (props: any) => {
  const [message, setMessage] = useState('')
  const [input, setInput] = useState('')
  const [data, setData] = useState<string[]>([])
  const [func, setFunc] = useState({data:{}})
  const fetchFunc = (address: string) => {
    return fetch(address).then(res => res.json())
  }
  useEffect(() => {
    fetchFunc('/api/func')
      .then((r) => setFunc(r))
  }, [data])
  const onChange = (e: any) => {
    setInput(e.target.value)
  }
  const onKeyPress = (e: any) => {
    if (e.key == 'Enter') {
      doAction(e)
    }
  }
  const doAction = (e: any) => {
    const res = eval(input)
    setMessage(res)
    data.unshift(input + ' = ' + res)
    setData(data)
    setInput('')
  }
  const clear = (e: any) => {
    setData([])
    setMessage('Clear history.')
  }
  const doFunc = (e: any) => {
    const arr = input.split(',')
    const fid = e.target.id
    const f = func.data[fid]
    const fe = eval(f.function)
    const res = fe(arr)
    setMessage(res)
    data.unshift(fid + ' = ' + res)
    setData(data)
    setInput('')
  }
  return (
    <div>
      <div className="alert alert-primary">
        <h5>Result: {message}</h5>
        <div className="form-group">
          <input type='text' value={input} className="form-control"
            onChange={onChange} onKeyPress={onKeyPress} />
        </div>
        {Object.entries(func.data).map((value, key) => 
          <button className="btn btn-secondary m-1" key={key}
            title={value[1].caption} id={value[0]} onClick={doFunc} >
              {value[0]}
          </button>
        )}
      </div>
      <table className="table">
        <thead><tr><th>History:</th></tr></thead>
        <tbody>
          {data.map((value, key) => 
            <tr key={key}><td>{value}</td></tr>
          )}
        </tbody>
      </table>
      <button onClick={clear} className="btn btn-warning">
        Clear History
      </button>
    </div>
  )
}

export default Calc

あとがき

特になし

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です