Reactアプリ100本ノックやってみた 「06 Profile」

# はじめに
フロントエンドに苦手意識のある私が、@Sicut_studyさんの【Reactアプリ100本ノック】をやってみました。
変なところがあれば教えていただきたいです。



今回の課題

https://qiita.com/Sicut_study/items/ccfc451360d3f22bde63

# やってみる
```App.tsx
import React, { useState, useRef } from "react";
import html2canvas from "html2canvas";

const App = () => {
  const [base64Images, setBase64Images] = useState<string[]>([]);
  const inputRef = useRef<HTMLInputElement>(null);
  const [name, setName] = useState("");
  const [date, setDate] = useState("");
  const [phone, setPhone] = useState("");

  const rectangleStyle = {
    width: "300px",
    height: "300px",
    border: "2px solid #000000",
    borderRadius: "10px",
  };

  const imageStyle = {
    width: "200px",
    height: "200px",
    borderRadius: "100px",
  };

  const buttonStyle = {
    color: "#fff",
    background: "#eb6100",
    width: "200px",
  };

  // お誕生日入力時の処理
  const handleDateChange = (event: {
    target: { value: React.SetStateAction<string> };
  }) => {
    setDate(event.target.value);
  };

  // プロフィール写真入力時の処理
  // 参考　https://zenn.dev/yuyan/articles/f35da08770a135
  const handleInputFile = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files) {
      return;
    }

    const fileArray = Array.from(files);
    const loadImages = new Array(fileArray.length);
    let loadCount = 0;

    fileArray.forEach((file, index) => {
      const reader = new FileReader();
      reader.onloadend = () => {
        const result = reader.result;
        if (typeof result !== "string") {
          return;
        }
        loadImages[index] = result;
        loadCount++;
        if (loadCount === fileArray.length) {
          setBase64Images((prevImages) => [...prevImages, ...loadImages]);
        }
      };
      reader.readAsDataURL(file);
    });
    if (inputRef.current) {
      inputRef.current.value = "";
    }
  };

  // PNG出力時の処理
  //参考　https://oldbigbuddha.dev/posts/react-component-to-png
  const onClickExport = () => {
    // 画像に変換する component の id を指定
    const target = document.getElementById("target-component");
    if (target) {
      html2canvas(target).then((canvas) => {
        const targetImgUri = canvas.toDataURL("img/png");
        saveAsImage(targetImgUri);
      });
    } else {
      console.error("target-componentが見つかりません");
    }
  };

  const saveAsImage = (uri: any) => {
    const downloadLink = document.createElement("a");

    if (typeof downloadLink.download === "string") {
      downloadLink.href = uri;

      // ファイル名
      downloadLink.download = "component.png";

      // Firefox では body の中にダウンロードリンクがないといけないので一時的に追加
      document.body.appendChild(downloadLink);

      // ダウンロードリンクが設定された a タグをクリック
      downloadLink.click();

      // Firefox 対策で追加したリンクを削除しておく
      document.body.removeChild(downloadLink);
    } else {
      window.open(uri);
    }
  };

  return (
    <div>
      <div style={{ display: "flex" }}>
        <div style={{ marginRight: "50px" }}>
          <div style={rectangleStyle} id="target-component">
            <div>
              {base64Images.length !== 0 &&
                base64Images.map((image, idx) => (
                  <div key={idx}>
                    <img src={image} style={imageStyle} />
                  </div>
                ))}
            </div>

            <div>お名前：{name}</div>
            <div>お誕生日：{date}</div>
            <div>電話番号：{phone}</div>
          </div>
        </div>
        <div>
          <div>
            <div>お名前</div>
            <input
              type="text"
              value={name}
              onChange={(e) => {
                setName(() => e.target.value);
              }}
            />
          </div>

          <div>
            <div>お誕生日</div>
            <input type="date" value={date} onChange={handleDateChange} />
          </div>

          <div>
            <div>電話番号</div>
            <input
              type="text"
              value={phone}
              onChange={(e) => {
                setPhone(() => e.target.value);
              }}
            />
          </div>

          <div>
            <div>プロフィール写真</div>
            <input
              type="file"
              accept="image/png"
              onChange={handleInputFile}
              ref={inputRef}
            />
          </div>

          <div>
            <button style={buttonStyle} onClick={() => onClickExport()}>
              PNG出力
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

export default App;

```



# 完成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621123/618e9318-b73d-656d-9b7b-5db01017ffa7.png)






# おわりに
ただただ要件を満たしただけのコードですが完了とします。やってみると分かってきたのですが、僕はロジックを考えるのは好きですが、デザインとかにはあまり興味がないようです。