はじめに
フロントエンドに苦手意識のある私が、@Sicut_studyさんの【Reactアプリ100本ノック】をやってみました。
変なところがあれば教えていただきたいです。
今回の課題
やってみる
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;
完成
おわりに
ただただ要件を満たしただけのコードですが完了とします。やってみると分かってきたのですが、僕はロジックを考えるのは好きですが、デザインとかにはあまり興味がないようです。
