react入門小demo(TS版本)
基於官方的入門demo進行TS改造
准備
初始化腳手架
// 執行以下命令就能看到在當前目錄my-app文件夾
npx create-react-app my-app --template typescript
找到my-app文件夾
// 啟動腳手架
yarn start
初始化demo
- 將以下代碼復制到app.css中
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
}
ol, ul {
padding-left: 30px;
}
.board-row:after {
clear: both;
content: "";
display: table;
}
.status {
margin-bottom: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
.square:focus {
outline: none;
}
.kbd-navigation .square:focus {
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
- 將js復制到app.ts中
class Square extends React.Component {
render() {
return (
<button className="square">
{/* TODO */}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square />;
}
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</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>
);
}
}
class Game extends React.Component {
render() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
export default Game;
為Props添加類型聲明
我們迎來了類型系統的第一頓毒打,系統告訴我們這一行,沒有匹配類型
(props: Readonly<{}>): Square
(props: {}, context?: any): Square
<Square value={i}/>
這里一行與上述的類型聲明不匹配,雖然我也看了TS一天,但是沒發現上面的語法,官方也沒看到和TS結合的例子,這個時候把錯誤信息復制到百度上去,解決方案就有了
只需要這樣
class Square extends React.Component<any>
解決了這個問題,我就發現,原來是因為我們Square組件的props類型沒聲明導致的,而reactReact.Component
// 使用接口描述對象的類型
interface SquareProps {
value: number
}
// 這里使用此接口
class Square extends React.Component<SquareProps> {
render() {
return (
<button className="square">
{ this.props.value }
</button>
);
}
}
我們的代碼正常運行了,繼續按照官網實例完善代碼
給組件添加交互功能
按照官方例子做,沒有出現什么問題
存儲點擊狀態
我們需要存儲單元格的點擊狀態,需要使用state,就像以下代碼
class Square extends React.Component {
constructor(props) {
// 這里必須的
super(props);
// 初始化state
this.state = {
value: null,
};
}
render() {
return (
<button className="square" onClick={() => alert('click')}>
{this.props.value}
</button>
);
}
}
使用state狀態
class Square extends React.Component {
constructor(props) {
// 這里必須的
super(props);
// 初始化state
this.state = {
value: null,
};
}
render() {
return (
<button className="square" onClick={() => this.setState({value: 'X'})}>
{this.state.value}
</button>
);
}
}
constructor props類型
新的類型錯誤出現了,props是一個隱式any類型,我們需要給它一個類型
class Square extends React.Component<SquareProps> {
// 這里加上props類型
constructor(props: SquareProps) {
super(props)
this.state = {
value: null
}
}
render() {
return (
<button className="square" onClick={() => this.setState({value: 'X'})}>
{ this.state.value }
</button>
);
}
}
state類型
state中沒有value,顯而易見的是,我們需要為state聲明類型,就想這樣
interface SquareProps {
value: number
}
interface SquareState {
value: string | null
}
class Square extends React.Component<SquareProps,SquareState> {
constructor(props: SquareProps) {
super(props)
this.state = {
value: null
}
}
render() {
return (
<button className="square" onClick={() => this.setState({value: 'X'})}>
{ this.state.value }
</button>
);
}
}
提取函數到類,設置函數類型
class Square extends React.Component<SquareProps,SquareState> {
constructor(props: SquareProps) {
super(props)
this.state = {
value: null
}
}
clickHandler = (e): void => {
this.setState({value: 'X'})
}
render() {
return (
<button className="square" onClick={(e) => this.clickHandler(e)}>
{ this.state.value }
</button>
);
}
}
event缺少類型,這里注意react的事件是合成事件不是原始事件
// error
clickHandler = (e: MouseEvent): void => {
this.setState({value: 'X'})
}
// correct
clickHandler = (e: React.MouseEvent): void => {
this.setState({value: 'X'})
}
將狀態提升到Board
這一步需要將狀態提升至Board組件
interface BoardProps {
}
interface BoardState {
squares: string []
}
class Board extends React.Component<BoardProps, BoardState> {
// 編寫constructor並且初始化state
constructor(props: BoardProps) {
super(props)
this.state = {
squares: Array(9).fill(null),
}
}
handlerClick= (i: number) =>{
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares});
}
renderSquare(i: number) {
// 傳遞onClick事件給子組件
return <Square value={this.state.squares[i] } onClick={() => this.handlerClick(i)}/>;
}
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</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>
);
}
}
刪除Square組件狀態
Square組件的狀態已經被提升到了Board組件,我們需要刪除對應的狀態
interface SquareProps {
value: string
onClick: () => void
}
// SquareState也可以刪除
interface SquareState {
value: string | null
}
class Square extends React.Component<SquareProps> {
// 刪除constructor
// constructor(props: SquareProps) {
// super(props)
// this.state = {
// value: null
// }
// }
// clickHandler也可以刪除
// clickHandler = (e: React.MouseEvent): void => {
// this.setState({value: 'X'})
// }
render() {
return (
<button className="square" onClick={(e) => this.props.onClick()}>
{ this.props.value }
</button>
);
}
}
改造Square組件為函數組件
// 由於 無狀態,所以改造成函數組件
function Square(props: SquareProps) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
總結
作為一個前端,Typescript主要被用在視圖層,Vue2和TS結合不太友好,特別是在this的處理上,React與TS結合體驗很好,適合用來學習TS,同時簡單了解一下react,開拓一下思路。