react入門小demo


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添加類型聲明

1sKKAK.png

我們迎來了類型系統的第一頓毒打,系統告訴我們這一行,沒有匹配類型

(props: Readonly<{}>): Square

(props: {}, context?: any): Square
<Square value={i}/> 

這里一行與上述的類型聲明不匹配,雖然我也看了TS一天,但是沒發現上面的語法,官方也沒看到和TS結合的例子,這個時候把錯誤信息復制到百度上去,解決方案就有了

1sMDIK.png

只需要這樣

class Square extends React.Component<any>

解決了這個問題,我就發現,原來是因為我們Square組件的props類型沒聲明導致的,而reactReact.Component 這種語法就可以聲明我們的props類型,那么我們不要使用any,any只在特殊情況下使用,否則類型系統就失去意義了 ,這里我們使用接口描述props類型

// 使用接口描述對象的類型
interface SquareProps {
  value: number
}
// 這里使用此接口
class Square extends React.Component<SquareProps> {
  render() {
    return (
      <button className="square">
        { this.props.value }
      </button>
    );
  }
}

1sltBR.png

我們的代碼正常運行了,繼續按照官網實例完善代碼

給組件添加交互功能

1s1e2D.png

按照官方例子做,沒有出現什么問題

存儲點擊狀態

我們需要存儲單元格的點擊狀態,需要使用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類型,我們需要給它一個類型

1s3Wp8.png

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類型

1s88gS.png

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>
    );
  }
}

1sBkOx.png

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,開拓一下思路。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM