React 入门小游戏学习记录

React - 用于构建用户界面的 JavaScript 库

React是一个声明式,高效且灵活的用于构建用户界面的JavaScript库。使用React可以将一些简短、独立的代码片段组合成复杂的UI界面,这些代码片段被称作“组件”。

1. 环境准备
// 创建项目
npx create-react-app my-app
cd my-app
npm start
2. React.Component
3. props 参数
4. render ()

render()方法返回需要展示在屏幕上的视图的层次结构。其返回值描述了你希望在屏幕上看到的内容。React根据描述,然后把结果展示出来。更具体地来说,render()返回了一个{React元素},这是一种对渲染内容的轻量级描述。

语法

会被编译成React.createElement(‘div’)。

class ShoppingList extends React.Component {
  render() {
    return (
      <div className="shopping-list">
        <h1>Shopping List for {this.props.name}</h1>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

// 用法示例: <ShoppingList name="Mark" />

上述的代码等同于:

return React.createElement('div', {className: 'shopping-list'},
  React.createElement('h1', /* ... h1 children ... */),
  React.createElement('ul', /* ... ul children ... */)
);
5. JSX
  • 特性
    • 可以使用任意JS表达式,只需用一个大括号把表达式括起来
    • 每一个React元素实际上都为一个JS对象,我们可以在程序中将它保存在变量中或者作为参数传递。
6. 井字小游戏相关代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
const root = ReactDOM.createRoot(document.getElementById('root'));

// react入门小游戏 -- 井字棋
// 创建Square组件
/* class Square extends React.Component {
  // 添加构造函数
  // constructor(props) {
  //   // 在JS class中定义其子类的构造函数时,都需要调用super方法。故,在所有含有构造函数的React组件中,构造函数必须以super(props)开头
  //   super(props);
  //   // 初始化私有属性
  //   this.state =  {
  //     value: null
  //   }
  // }
  render() {
    return (
      // 注意绑定函数时 () => , 若写成onClick{alert('click')} 会导致每次这个组件渲染时都会触发弹出框
      // 
      <button
        className="square"
        //调用this.setState 通知React去重新渲染Square组件
        onClick={() => this.props.onClick()}>
        {this.props.value}
      </button>
    );
  }
} */
// 因Square组件中只包含render(), 且没有state,故将其转写为函数组件
function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  )
}
// 创建Board 组件
class Board extends React.Component {
  /* constructor (props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
      xIsNext: true,
    };
  } */
  /* handleClick (i) {
    // 创建squares数组的一个副本
    const squares =  this.state.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      squares: squares,
      xIsNext: !this.state.xIsNext,
    });
  } */
  renderSquare(i) {
    // 创建一个名为value的prop到子组件Square中
    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>
    );
  }
}

// 创建Game组件
class Game extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      history: [
        {squares: Array(9).fill(null)},
      ],
      xIsNext: true,
      stepNumber: 0, // 表示当前正在查看的历史记录
    }
  }
  jumpTo (step) {
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0,
    })
  }

  handleClick (i) {
    const history = this.state.history.slice(0, this.state.stepNumber + 1);
    const current = history[history.length - 1];
    const squares =  current.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      history: history.concat([
        {squares: squares}
      ]),
      xIsNext: !this.state.xIsNext,
      stepNumber: history.length
    });
  }
  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>
    );
  }
}

// 判断胜出者
function calculateWinner(squares) {
  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
}

// ========================================
root.render(
  <Game />,
);
7. 注意
  • 为了少输入代码,同时为了避免 this 造成的困扰,在这里使用箭头函数 来进行事件处理

  • JavaScript class 中,每次你定义其子类的构造函数时,都需要调用 super 方法。因此,在所有含有构造函数的的 React 组件中,构造函数必须以 super(props) 开头。

  • 当你遇到需要同时获取多个子组件数据,或者两个组件之间需要相互通讯的情况时,需要把子组件的 state 数据提升至其共同的父组件当中保存。之后父组件可以通过 props 将状态数据传递到子组件当中。这样应用当中所有组件的状态数据就能够更方便地同步共享了。

  • 为了提高可读性,我们把返回的 React 元素拆分成了多行,同时在最外层加了小括号,这样 JavaScript 解析的时候就不会在 return 的后面自动插入一个分号从而破坏代码结构了。

  • 因为 DOM 元素 <button> 是一个内置组件,因此其 onClick 属性在 React 中有特殊的含义。而对于用户自定义的组件来说,命名就可以由用户自己来定义了。我们给 Square 的 onClick 和 Board 的 handleClick 赋予任意的名称,代码依旧有效。在 React 中,有一个命名规范,通常会将代表事件的监听 prop 命名为 on[Event],将处理事件的监听方法命名为 handle[Event] 这样的格式。

8. React中的不可变性

改变数据的方式:

(1)直接修改变量的值

(2)不直接修改,使用新数据替换旧数据

不直接修改(或改变底层数据)的好处:

  • 简化复杂的功能

  • 跟踪数据的改变

    若直接修改数据,很难跟踪到数据的改变。跟踪数据的改变需要可变对象可以与改变之前的版本进行对比,这样整个对象树都需要被遍历一次。

    跟踪不可变数据的变化相对较易,若发现对象变成了一个新对象,就可以说对象发生了改变。

  • 确定在React中何时重新渲染

    不可变性主要的优势在于它可以帮助我们在React中创建pure components,我们可以很轻松的确定不可变数据是否发生了改变,从而确定何时对组件进行重新渲染。

9. 函数组件

​ 如果你想写的组件只包含一个render()方法,并且不包含state时,使用函数组件会更简单。我们无需定义一个继承于React.Componen的类,可以定义一个函数,将props作为参数,并return需要渲染的元素。示例如下:

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}
10. 时间旅行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值