Open7
memo SampleApp with React + TypeScript + Vite
React + TypeScript + Vite でアプリを作成してみる
Vite
React: 何となく触ったことある
TypeScript: JavaScript なら触ったことある
Vite: なんか速いらしい
npm create vite
npm install
npm run dev
o [+ Enter](ブラウザを開く)
q [+ Enter](サーバーを?終了する)
reactとtypescriptを選択
svelteとか速くて簡単って聞いたけどどうなんだろうか
hello react
Q.何を作る? A.ぷよぷよ連鎖シミュレーター
まずはApp.tsxをきれいにしました。
App.tsx
import './App.css'
function App() {
return (
<>
</>
)
}
export default App
フィールドと、フィールドの1マスを作成
とりあえず表示するだけ
Reactはコンポーネントと呼ばれるUIの部品を組み合わせていくらしい?
ボトムアップで作成していく
Field.tsx
const Cell = () => {
return (
<>
<p>1マス</p>
</>
)
}
export const Field = () => {
return (
<>
<Cell />
</>
)
}
とりあえず表示できた。
Cellのデータをボタンに変更
四角くする
見た目をいい感じにする
それっぽい見た目になってきた
Field.css
.cell__button {
height: 50px;
width: 50px;
padding: 0;
border-width: 1px;
}
.cell__button:hover {
opacity: 70%;
}
.field {
display: grid;
grid-template-columns: repeat(6, 50px);
}
Field.tsx
import { useState } from 'react';
import './Field.css'
const Height: number = 13;
const Width: number = 6;
type CellData = {
id: number;
}
const Cell = (props: {id: number}) => {
return (
<>
<button key={props.id} className="cell__button">{props.id}</button>
</>
)
}
const initCellData = (h: number, w: number): CellData[] => {
let data: CellData[] = [];
for(let i = 0; i < h; ++i) {
for(let j = 0; j < w; ++j) {
const id = w * i + j;
data.push({ id });
}
}
return data;
}
export const Field = () => {
const [cells, setCells] = useState<CellData[]>(() => initCellData(Height, Width));
return (
<>
<div className='field'>
{cells.map((cell) => {
return <Cell id={cell.id}/>
})}
</div>
</>
)
}
いろいろあってこうなった
やったこと
- CSSの改造
- 後述するcolorプロパティに応じて色が変更
- onclickイベントの実装
- props には nullable な関数オブジェクトとして? onclick?: () => void を追加
- Fieldコンポーネント内でCellに渡すときに () => handleCellClick(cell.id) とした
cssがムズすぎる。全然思ったように言うことを聞かない。
const [selectedColor, setSelectedColor] = useState<number>(0);
const handleCellClick = (id: number) => {
setCells((cells) => {
let newCells = [...cells];
newCells[id].color = selectedColor;
setSelectedColor((selectedColor + 1) % 7);
return newCells;
});
}
選択されている色のuseState
この値に応じて、クリックしたCellの色が変わる。今はろーてーとしてるだけ。
余談だが、0の時は空白、1の時はおじゃまぷよの予定。2の時は色ぷよになるか、それとも固ぷよ(周囲4マスを1回消すとおじゃまぷよになるやつ)にした方が筋がいい気もする。
余談の余談だが、空白+おじゃまぷよ2種類+5色でちょうど8なので3bitに収まる。
3 * 13 * 6 = 234 bit なのでymm 256bit レジスタにいい感じに乗る。
App.tsx全体
import { useState } from 'react';
import './Field.css'
const Height: number = 13;
const Width: number = 6;
type CellData = {
id: number;
color: number;
onclick?: () => void;
}
const num2color: string[] = [
"red",
"blue",
"yellow",
"green",
"purple",
];
const Cell = (props: {id: number, color: number, onclick: () => void }) => {
let back: string = props.color > 1 ? "cell__circle" : "";
if (props.color >= 2) {
back += ` ${num2color[props.color - 2]}-circle`;
}
return (
<>
<button key={props.id} className="cell__button" onClick={() =>props.onclick()}>
<div className={back}></div>
<span className="cell__symbol">{props.color}</span>
</button>
</>
)
}
const initCellData = (h: number, w: number): CellData[] => {
let data: CellData[] = [];
for(let i = 0; i < h; ++i) {
for(let j = 0; j < w; ++j) {
const id = w * i + j;
const color = 0;
data.push({ id, color });
}
}
return data;
}
export const Field = () => {
const [cells, setCells] = useState<CellData[]>(() => initCellData(Height, Width));
const [selectedColor, setSelectedColor] = useState<number>(0);
const handleCellClick = (id: number) => {
setCells((cells) => {
let newCells = [...cells];
newCells[id].color = selectedColor;
setSelectedColor((selectedColor + 1) % 7);
return newCells;
});
}
return (
<>
<div className='field'>
{cells.map((cell) => {
return <Cell key={cell.id} id={cell.id} color={cell.color} onclick={() => handleCellClick(cell.id)}/>
})}
</div>
</>
)
}
App.css全体
.cell__button {
height: 50px;
width: 50px;
padding: 0;
border-width: 1px;
position: relataive;
display: flex;
justify-content: center;
align-items: center;
}
.cell__button:hover {
opacity: 70%;
}
.cell__circle {
content: "";
position: absolute;
width: 40px;
height: 40px;
margin: 0;
border-radius: 50%;
}
.red-circle {
background-color: orangered;
}
.blue-circle {
background-color: cyan;
}
.yellow-circle {
background-color: yellow;
}
.green-circle {
background-color: limegreen;
}
.purple-circle {
background-color: magenta;
}
.cell__symbol {
position: absolute;
font-size: x-large;
font-weight: bold;
font-family: Arial, Helvetica, sans-serif;
}
.field {
display: grid;
grid-template-columns: repeat(6, 50px);
}
nullableな関数の実行
f?.();