React チュートリアル TypeScriptのフック版
Reactのチュートリアルを、TypeScriptでフックを使用して実装コードになります。
React MAIN CONCEPTS の確認
- 1. Hello World
- 2. JSX の導入
- 3. 要素のレンダー
- 4. コンポーネントと props
- 5. state とライフサイクル
- 6. イベント処理
- 7. 条件付きレンダー
- 8. リストと key
- 9. フォーム
- 10. state のリフトアップ
- 11. コンポジション vs 継承
- 12. React の流儀
React ドキュメントを読むのに必要最小限の知識
- We define variables with
let
andconst
statements. For the purposes of the React documentation, you can consider them equivalent tovar
.- 変数は、let and constステートメントで定義します。Reactのドキュメントでは、それらをと varと同等と見なすことができます。
- We use the
class
keyword to define JavaScript classes. There are two things worth remembering about them. Firstly, unlike with objects, you don’t need to put commas between class method definitions. Secondly, unlike many other languages with classes, in JavaScript the value ofthis
in a method depends on how it is called.- classキーワードを使用してJavaScriptクラスを定義します。それらについて覚えておく価値のあることが2つあります。まず、オブジェクトの場合とは異なり、クラスメソッド定義の間にコンマを置く必要はありません。次に、クラスを持つ他の多くの言語とは異なり、JavaScript thisでは、メソッドの値はそれがどのように呼び出されるかに依存します。
- We sometimes use
=>
to define “arrow functions". They’re like regular functions, but shorter. For example,x => x * 2
is roughly equivalent tofunction(x) { return x * 2; }
. Importantly, arrow functions don’t have their ownthis
value so they’re handy when you want to preserve thethis
value from an outer method definition.- 「矢印関数」の=>を定義に使用することがあります。これらは通常の関数のようですが、短くなっています。たとえば、x => x * 2 は、function(x) { return x * 2; } とほぼ同等です。重要なことに、矢印関数には独自の this値がないため、外部メソッド定義の this値を保持する場合に便利です。
チュートリアル:Reactの導入 (TypeScript版)
TypeScriptのアプリケーション雛形を作成します。
npx create-react-app rewrite-react-tutorial-in-hooks --template typescript
index.css
と index.tsx
を、スターターコードに置き換えします。
コンパイルエラー
Parameter 'i' implicitly has an 'any' type. TS7006
class Board extends React.Component {
renderSquare(i) {
^
return <Square />;
}
型を指定してコンパイルエラーを解消します。
renderSquare(i: number) {
データをPropsを経由で渡す
interface SquareProps {
value: number;
}
class Square extends React.Component<SquareProps> {
インタラクティブなコンポーネントを作る
interface SquareState {
value: string | null;
}
class Square extends React.Component<SquareProps, SquareState> {
constructor(props:any) {
super(props);
this.state = {
value: '',
};
}
render() {
return (
<button className="square" onClick={() => this.setState({value: 'X'})}>
{this.state.value}
</button>
);
}
}
index.tsx
(TypeScript版の完成形)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
interface SquareProps {
value: any;
onClick: () => void;
}
function Square(props: SquareProps) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
interface BoardState {
squares: ('O' | 'X' | null)[];
onClick: (i: number) => void;
}
class Board extends React.Component<BoardState, {}> {
renderSquare(i: number) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
render() {
return (
<div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
interface HistoryData {
squares: ('O' | 'X' | null)[];
}
interface GameState {
history: HistoryData[];
xIsNext: boolean;
stepNumber: number;
}
class Game extends React.Component<{}, GameState> {
constructor(props: any) {
super(props);
this.state = {
history: [{
squares: Array(9).fill(null),
}],
stepNumber:0,
xIsNext: true,
};
}
handleClick(i: number) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
let squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? 'X': 'O';
this.setState({
history: history.concat([{
squares,
}]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext,
});
}
jumpTo(step: number) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0,
});
}
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move} >
<button onClick={() => this.jumpTo(move)}> {desc}</button>
</li>
)
});
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={(i) => this.handleClick(i)}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
}
ReactDOM.render(
<Game />,
document.getElementById('root')
);
function calculateWinner(squares: any) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
フックで置き換え
import React, { useState } from 'react';
const Game = () => {
const [history, setHistory] = useState([{
squares: Array(9).fill(null)
}])
const [stepNumber, setStepNumber] = useState(0)
const [xIsNext, setXIsNext] = useState(true)
const handleClick = (i: number) => {
const new_history = history.slice(0, stepNumber + 1);
const current = new_history[new_history.length - 1];
let squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = xIsNext ? 'X': 'O';
setHistory(new_history.concat([{squares}]));
setStepNumber(new_history.length);
setXIsNext(!xIsNext);
}
const jumpTo = (step: number) => {
setStepNumber(step)
setXIsNext((step % 2) === 0)
}
index.tsx
(TypeScriptフック版の完成形)
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
interface SquareProps {
value: any;
onClick: () => void;
}
function Square(props: SquareProps) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
interface BoardState {
squares: ('O' | 'X' | null)[];
onClick: (i: number) => void;
}
const Board = (props: BoardState) => {
const renderSquare = (i: number) => {
return (
<Square
value={props.squares[i]}
onClick={() => props.onClick(i)}
/>
);
}
return (
<div>
<div className="board-row">
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="board-row">
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="board-row">
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
</div>
)
}
const Game = () => {
const [history, setHistory] = useState([{
squares: Array(9).fill(null)
}])
const [stepNumber, setStepNumber] = useState(0)
const [xIsNext, setXIsNext] = useState(true)
const handleClick = (i: number) => {
const new_history = history.slice(0, stepNumber + 1);
const current = new_history[new_history.length - 1];
let squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = xIsNext ? 'X': 'O';
setHistory(new_history.concat([{squares}]));
setStepNumber(new_history.length);
setXIsNext(!xIsNext);
}
const jumpTo = (step: number) => {
setStepNumber(step)
setXIsNext((step % 2) === 0)
}
const current = history[stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move} >
<button onClick={() => jumpTo(move)}> {desc}</button>
</li>
)
});
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (xIsNext ? 'X' : 'O');
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={(i) => handleClick(i)}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
ReactDOM.render(
<Game />,
document.getElementById('root')
);
function calculateWinner(squares: any) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
その他
Visual Studio Code で React の開発
https://code.visualstudio.com/docs/nodejs/reactjs-tutorial