React 数字のボタンを表示し押下で数字が打ち込まれるコンポーネント作成

## はじめに
React初心者が頑張って、数字の打ち込みができるコンポーネントを作りました。
他になにか作るときにも使えるので自分用にここに残しておきます。

## 早速本題

親コンポーネント（Index.jsx）
```jsx
import React, { useState, useEffect } from 'react';
import { Link, useForm, usePage } from '@inertiajs/react';
import FormInputMath from '@/Components/FormInputMath';

const Index = ({ sortings }) => {

    // 送信するデータ
    const { data, setData, post, processing, errors } = useForm({
        kari_price: '',
        kashi_price: '',
    });

    // 送信ボタン押下で指定のルートにデータを飛ばす
    const handleSubmit = (e) => {
        e.preventDefault();
        post(route('sorting'));
    };

    // setDataでdataを更新
    const handleInputChange = (name, value) => {
        setData(name, value);
    };
    
//(略)

// コンポーネントの使用　nameとonChangeをpropsで渡す
<FormInputMath
    name="kari_price"
    onChange={handleInputChange}
/>

//(略)
<FormInputMath
    name="kashi_price"
    onChange={handleInputChange}
/>

//(略)
```

子コンポーネント (FormInputMath.jsx)
```jsx
import React, { useState } from 'react';

const FormInputMath = ({ onChange, name }) => {
    const [inputValue, setInputValue] = useState('');
    const [showButtons, setShowButtons] = useState(false);

    // ボタン押下で数値を追加する関数
    const handleButtonClick = (e, number) => {
        e.preventDefault();  // フォーム送信を防ぐ form内のbuttonはtypeをしてしないとsubmitになるみたい。type=buuttonと記述すればこの一行は必要ない。
        const newValue = inputValue + number;
        // inputValueの更新
        setInputValue(newValue);
        if (onChange) {
            // 親コンポーネントから渡ったonChangeを発火させる
            onChange(name, newValue); 
        }
    };

    // inputが押下されたときにボタン表示を切り替える関数
    const handleInputClick = () => {
        setShowButtons(!showButtons);
    };

    // キーボード入力を処理する関数を追加
    const handleInputChange = (e) => {
        const value = e.target.value;
        // 数字のみを許可する正規表現
        if (/^\d*$/.test(value)) {
            setInputValue(value);
            if (onChange) {
                // 親コンポーネントから渡ったonChangeを発火させる
                onChange(name, value);
            }
        }
    };

    // バックスペース機能を追加
    const handleBackspace = (e) => {
        e.preventDefault();
        const newValue = inputValue.slice(0, -1);
        setInputValue(newValue);
        if (onChange) {
            // 親コンポーネントから渡ったonChangeを発火させる
            onChange(name, newValue);
        }
    };

    return (
        <div>
            <input
                // 親から渡ったnameをはめる
                name={name}
                className='form-control form-control-sm'
                type="text"
                value={inputValue}
                onClick={handleInputClick} // 表示・非表示制御
                onChange={handleInputChange}  //　キーボード入力の処理
            />

            {showButtons && (
                <div style={{ display: 'flex', flexWrap: 'wrap' }}>
                    {[...Array(10)].map((_, index) => (
                        // ０～９の数字を展開
                        <button
                            className='button'
                            key={index}
                            onClick={(e) => handleButtonClick(e, index)} // 数字押下で押した数字を末尾に加える処理
                        >
                            {index}
                        </button>
                    ))}
                    <button
                        className='button'
                        onClick={handleBackspace} // ←　押下で末尾の数字を削除
                    >
                        ←
                    </button>
                </div>
            )}
        </div>
    );
};

export default FormInputMath;

```

cssは別途用意してくださいね！


## 追記

更新処理の際の記述と注意点もまとめておきます。
親コンポ―ネント　Index.jsx
```diff_jsx
import React, { useState, useEffect } from 'react';
import { Link, useForm, usePage } from '@inertiajs/react';
import FormInputMath from '@/Components/FormInputMath';

const Index = ({ sortings }) => {

    // 送信するデータ
    const { data, setData, post, processing, errors } = useForm({
-        kari_price: '',
-        kashi_price: '',
+        kari_price: sortings.kari_price,
+        kashi_price: sortings_kashi_price,
    });

    // 送信ボタン押下で指定のルートにデータを飛ばす
    const handleSubmit = (e) => {
        e.preventDefault();
        post(route('sorting'));
    };

    // setDataでdataを更新
    const handleInputChange = (name, value) => {
        setData(name, value);
    };
    
//(略)

// コンポーネントの使用　nameとonChangeをpropsで渡す
<FormInputMath
+   value={data.kari_price}
    name="kari_price"
    onChange={handleInputChange}
/>

//(略)
<FormInputMath
+   value={data.kashi_price}
    name="kashi_price"
    onChange={handleInputChange}
/>

//(略)
```
子コンポーネント FormInputMath.jsx
```diff_jsx
- import React, { useState } from 'react';
+ import React, { useState, useEffect } from 'react';

- const FormInputMath = ({ onChange, name }) => {
+ const FormInputMath = ({ onChange, name, value }) => {
 
-    const [inputValue, setInputValue] = useState('');
+    const newValue = inputValue +  String(number); //文字列に変換しないと加算される
    const [showButtons, setShowButtons] = useState(false);

    // ボタン押下で数値を追加する関数
    const handleButtonClick = (e, number) => {
        e.preventDefault();  
-        const newValue = inputValue + number;
+        const newValue = inputValue +  String(number); //文字列に変換しないと加算される

+    // valueの監視、更新
+    useEffect(() => {
+        setInputValue( String(value || '')); //更新するデータをはめ込む
+    }, [value]);
    
        // inputValueの更新
        setInputValue(newValue);
        if (onChange) {
            // 親コンポーネントから渡ったonChangeを発火させる
            onChange(name, newValue); 
        }
    };

    // inputが押下されたときにボタン表示を切り替える関数
    const handleInputClick = () => {
        setShowButtons(!showButtons);
    };

    // キーボード入力を処理する関数を追加
    const handleInputChange = (e) => {
        const value = e.target.value;
        // 数字のみを許可する正規表現
        if (/^\d*$/.test(value)) {
            setInputValue(value);
            if (onChange) {
                // 親コンポーネントから渡ったonChangeを発火させる
                onChange(name, value);
            }
        }
    };

    // バックスペース機能を追加
    const handleBackspace = (e) => {
        e.preventDefault();
        const newValue = inputValue.slice(0, -1);
        setInputValue(newValue);
        if (onChange) {
            // 親コンポーネントから渡ったonChangeを発火させる
            onChange(name, newValue);
        }
    };

    return (
        <div>
            <input
                // 親から渡ったnameをはめる
                name={name}
                className='form-control form-control-sm'
                type="text"
                value={inputValue}
                onClick={handleInputClick} // 表示・非表示制御
                onChange={handleInputChange}  //　キーボード入力の処理
            />

            {showButtons && (
                <div style={{ display: 'flex', flexWrap: 'wrap' }}>
                    {[...Array(10)].map((_, index) => (
                        // ０～９の数字を展開
                        <button
                            className='button'
                            key={index}
                            onClick={(e) => handleButtonClick(e, index)} // 数字押下で押した数字を末尾に加える処理
                        >
                            {index}
                        </button>
                    ))}
                    <button
                        className='button'
                        onClick={handleBackspace} // ←　押下で末尾の数字を削除
                    >
                        ←
                    </button>
                </div>
            )}
        </div>
    );
};

export default FormInputMath;

```

※注意点として
子コンポーネントのconst newValue = inputValue +  String(number); で
numberを今回文字列に変換している。

更新処理の場合、データに入っているのは数値型なので
数字型　+　数字型　になり加算されてしまう。

１＋１＝２
けどボタンをおしたら末尾に入るようにしたいので文字列に変換する。

１＋"1"＝"１１"


ではなぜ更新処理で急に加算になったのか？
今までは動いたのか？
これは最初の登録処理の際
""＋１＝"１" このように最初は空文字で文字列扱いはので結果は文字列の１に変換されるからだ。

これはハマりそう・・・