Chuyển đến nội dung chính

Bài 14: ReactJs- Viết game caro bằng React


Hôm nay, chúng ta sẽ cùng nhau xây dựng game cờ caro dựa trên những kiến thức đã học ở bài trước nha. Các bạn có thể check kết quả tại đây: Final Result. Bạn hãy thử chơi game để biết chúng ta sẽ xây dựng game như thế nào.
Để có thể hiểu được bài này, các bạn nên xem qua các bài trước của mình và đã setup môi trường như bài 1 này: Bài 1: ReactJs - Environment Setup. Nếu như mọi thứ đã ok hết rồi thì ta bắt đầu nào.

Starter Code:

Trong file index.css ta thêm các style sau:
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;
}

CSS khá dễ hiểu các bạn tự xem nha.
Trong file index.js:

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

// ========================================

ReactDOM.render(
  <Game />,
  document.getElementById('root')
);

Ở đây, ta tạo 3 component đó là Square, Board và Game.
Square Component sẽ render ra button hình ô vuông, mỗi ô vuông có thể xem như 1 nước đi, Board là bảng sẽ render  ra 9 Square, Game là component cha chứa Board. Sau khi chạy chương trình npm start các bạn sẽ có kết quả sau: Result.

Passing Data Through Props

Nếu các bạn chưa biết về Props xem qua bài này: Bài 5: ReactJS - Props Overview. Bây giờ ta truyền data từ Board xuống cho Square. Trong renderSquare ta thay đổi như sau:
class Board extends React.Component {
  renderSquare(i) {
    return <Square value={i} />;
  }
Sau đó thay đổi hàm render trong Square:
class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {this.props.value}
      </button>
    );
  }
}
Kết quả ta được:

An Interactive Component

Bây giờ chúng ta sẽ làm cho giá trị của Square thay đổi thành X mỗi khi ta click vào ô vuông. Đầu tiên ta thử đoạn code sau:
class Square extends React.Component {
  render() {
    return (
      <button className="square" onClick={() => alert('click')}>
        {this.props.value}
      </button>
    );
  }
}
Nếu bạn click vào ô vuông và xuất hiện alert thì ok.
Bây giờ chúng ta sẽ tạo state cho thằng Square để chứa giá trị và thay đổi mỗi khi ta click phải.
class Square extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: null,
    };
  }

  render() {
    return (
      <button className="square" onClick={() => alert('click')}>
        {this.props.value}
      </button>
    );
  }
}
Trong JavaScript classes, chúng ta cần gọi hàm super(); khi chúng ta tạo constructor cho subclass.
Bây giờ ta thay đổi hàm render của Square để hiển thị value là state hiện tại và thay đổi nó mỗi khi có click.
  • Thaythis.props.value bằngthis.state.value trong <button> tag.
  • Thay () => alert() event bằng () => this.setState({value: 'X'}).

Kết quả đoạn code như sau:
class Square extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: null,
    };
  }

  render() {
    return (
      <button className="square" onClick={() => this.setState({value: 'X'})}>
        {this.state.value}
      </button>
    );
  }
}
Bất cứ khi nào this.setState được gọi, React sẽ tự động cập nhập lại thằng Square re-render lại. Khi thành phần re-renders, this.state.value sẽ thay đổi thành 'X' vì vậy bạn sẽ thấy một X xuất hiện trong mạng lưới các ô vuông.
Nếu bạn click vào bất kì hình vuông nào, nó sẽ xuất hiện dấu X.

Developer Tools

Nếu các bạn đã xem Bài 13: ReactJS - Tìm hiểu React Extension, công cụ không thể thiếu cho React Developer thì đây là lúc thích hợp để bạn sử dụng Tool này, giờ các bạn có thể inspect các component , xem các props và state mà component đó nắm giữ để hiểu hơn về React Component.

Lifting State Up

Chúng ta đã cơ bản tạo được giao diện game rồi đấy. Nhưng hiện tại mỗi thằng Square giữ một state riêng, để hoàn thiện game chúng ta cần kiểm tra nếu một player win game và ta cần thay đổi nước đi xen kẽ X, O. Để làm được như vậy chúng ta cần chứa 9 states của Square đó ở một component thay vì mỗi component như trước. Và Board component sẽ đảm nhận trọng trách này, chúng ta sẽ tạo một mảng states gồm 9 phần tử tượng trưng cho 9 states của Square, sau đó pass nó xuống Square thông qua props. Ta thay đổi thằng Board như sau:

class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
    };
  }

  renderSquare(i) {
    return <Square value={this.state.squares[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>
    );
  }
}
Bây giờ ta cần xử lý khi 1 Square được click, việc click sẽ thay đổi state trong Board , bởi vì chúng ta không thể update state của Board một cách trực tiếp từ Square nên ta sẽ pass 1 function từ Board xuống Square để thực hiện điều này. Ta thay đổi renderSquare như sau:
renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(i)}
      />
    );
  }
Board pass 2 props xuống Square: value and onClick. Và giờ chúng ta sẽ xử lý chúng trong Square. 
Ta thay đổi như sau:
  • Thay this.state.value bằng this.props.value trong Square’s render.
  • Thay this.setState() bằng this.props.onClick() trong Square’s render.
  • Xóa constructor() vì ta không dùng state nữa.
Sau khi thay đổi đoạn code sẽ như sau:
class Square extends React.Component {
  render() {
    return (
      <button className="square" onClick={() => this.props.onClick()}>
        {this.props.value}
      </button>
    );
  }
}
Bây giờ khi Square được click, nó sẽ gọi onClick function được truyền xuống từ Board. Đây là quá trình code thực hiện, các bạn chịu khó đọc english để hiểu rõ quá trình thực hiện:
  • The onClick prop on the built-in DOM <button> component tells React to set up a click event listener.
  • When the button is clicked, React will call the onClick event handler defined in Square’s render() method.
  • This event handler calls this.props.onClick(). Square’s props were specified by the Board.
  • Board passed onClick={() => this.handleClick(i)} to Square, so, when called, it runs this.handleClick(i) on the Board.
  • We have not defined the handleClick() method on the Board yet, so the code crashes.
Giờ ta cần tạo function handleClick trong Board component:
class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
    };
  }

  handleClick(i) {
    const squares = this.state.squares.slice();
    squares[i] = 'X';
    this.setState({squares: squares});
  }

  renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(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>
    );
  }
}
Chúng ta gọi hàm .slice() để copy squares  ra 1 biến tạm sau đó thay đổi biến tạm đó rồi gán ngược lại state khi thay đổi xong. Chúng ta sẽ tìm hiểu việc này ở bài kế.
Bây giờ bạn có thể test lại game trên màn hình, cách chơi vẫn như cũ nhưng giờ đây state không còn được lưu ở Squares nữa mà thay vào đó là Board. Việc này giúp ta có thể xây dựng game tiếp, các bạn để ý là mỗi lần ta update Board's State React sẽ auto chạy lại hàm render  giúp ta cập nhật các Squares. Các bạn có thể dùng React Extension để inspect các component để hiểu rõ những gì ta làm nãy giờ:
 Viết game caro bằng React
 Viết game caro bằng React
 Viết game caro bằng React


Nhận xét

  1. Trên đây là Top 9 phòng khám da liễu uy tín và chất lượng nhất TP.HCM. Hy vọng, bài viết sẽ giúp bạn có thêm những thông tin hữu ích để có thể lựa chọn cho mình một phòng khám da liễu phù hợp với nhu cầu của bản thân. Nếu bạn có bất kỳ thắc mắc gì cần giải đáp, hãy liên hệ ngay với chúng tôi để được tư vấn và hỗ trợ kịp thời nhé!
    Chúc bạn ngày càng biết yêu thương, trân trọng bản thân để luôn xinh đẹp và tự tin trong cuộc sống!
    Nguồn: https://bom.so/MVUZ29
    Nguồn: https://thammyvienula.vn/phong-kham-da-lieu-uy-tin-tp-hcm/
    Nguồn: https://thammyvienula.vn/
    TOP #9 Phòng khám da liễu uy tín nhất TPHCM – Bạn đã biết?


    Trả lờiXóa

Đăng nhận xét

Bài đăng phổ biến từ blog này

Xdebug, PhpStorm and Docker - Why it not working?

  Lately, i start new job with Magento, and while setup IDE for project i face problem with Xdebug, PhpStorm and Docker. It took me a lot of hours to find out and make it work. So i write this post to save some step for you guys also me some way to solve the stuck when we got. 1. How Xdebug work: Link  i founded this article with quite easy understand explanation how xdebug work, so spend some minutes to read it, we need to understans the thing we do to easy to solve it. 2: Define Xdebug is installed on server: With php -v you should see Xdebug showed. And with phpinfo() If you dont see it showed, it mean you have not installed it or it not enable Checking if you have enable extension from your php ini. Or if you have not installed it, consider its document: Link . 3. Now if you make sure xdebug installed but your break point at PhpStorm not break, continue these steps: In phpinfo(), make sure  xdebug.remote_enable is On cause you are using docker container, also checking...

5 minute setup Firebase for .NET C#

  Step 1: - Access firebase console and create a project: - At the project you just created, go to Firestore Database and create a collection you want: - Next, go to the project setting: - At tab  Service account generate your private key. Step 2: - Create a C# project. - Use Nuget to install following packages: - Create folder to store private key. - Finally, the code to make everything run: using Google.Apis.Auth.OAuth2; using Google.Cloud.Firestore; using Google.Cloud.Firestore.V1; using Google.Cloud.Storage.V1; using Grpc.Auth; using Grpc.Core; using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace FireBaseConnect {     class Program     {         static void Main(string[] args)         {             MyFireStore myfs = new MyFireStore();             myfs.GetLicense().Wait();         }...

Tổng hợp danh sách các trang web lấy backlink cực tốt (P.1)

Đi backlink  trong SEO là điều mà ai cũng phải biết và đầu tư cho nó nhiều nhất. Dưới đây, mình chia sẻ các trang web có thể đi backlink khá tốt cho mọi người. Mỗi ngày các bạn tạo 1 bài post sau đó post lên 10 trang trong tổng số 40 trang này, hôm sau cũng viết 1 bài khác rồi post lên 10 trang kế, khi hết thì quay vòng lại 10 trang đầu, mình sẽ update thêm các website nên các bạn cứ yên tâm không lo hết. (Các bạn nhớ bookmark lại kẻo quên trang mình nha :) ) Nếu các bạn không rõ về DA PA IN-EXTERNAL Links thì xem ở đây:  Các chỉ số quan trọng cần biết khi làm SEO ID URL DA PA Internal Links External Links Alexa Global Rank Alexa Local Rank 1 http://diendan.zing.vn/ 63,17 54,5 168 19 701 9 2 http://vatgia.com/ 62,61 57,34 984 23 8086 71 3 http://forum.ueh.edu.vn/ 49,82 38,88 555 43 92060 774 4 http://www.5giay.vn/ 47,12 55,72 445 ...