react1

React是一个用于构建用户界面的声明性,高效且灵活的JavaScript库。它允许您从称为“组件”的小而孤立的代码片段中组合复杂的UI。

React有几种不同的组件,但我们将从React.Component子类开始

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> ); } } // Example usage: <ShoppingList name="Mark" />

我们很快就会得到有趣的类似XML的标签。我们使用组件告诉React我们想要在屏幕上看到什么。当我们的数据发生变化时,React将有效地更新和重新渲染我们的组件。

这里,ShoppingList是一个React组件类,或React组件类型组件接受称为props(“属性”的缩写)的参数,并返回要通过该render方法显示的视图层次结构

render方法返回您想要在屏幕上看到的内容描述React接受描述并显示结果。特别是,render返回一个React元素,它是渲染内容的轻量级描述。大多数React开发人员使用称为“JSX”的特殊语法,这使得这些结构更容易编写。<div />语法在构建时被转化React.createElement('div')上面的例子相当于:

return React.createElement('div', {className: 'shopping-list'}, React.createElement('h1', /* ... h1 children ... */), React.createElement('ul', /* ... ul children ... */) );

通过道具传递数据

为了让我们的脚湿透,让我们尝试将一些数据从我们的Board组件传递到Square组件。

在Board的renderSquare方法中,更改代码以传递调用valueSquare 的道具

class Board extends React.Component { renderSquare(i) {  return <Square value={i} />; }

render通过替换{/* TODO */}{this.props.value}更改Square的方法来显示该值

class Square extends React.Component { render() { return ( <button className="square">  {this.props.value} </button> ); } }
class Square extends React.Component { render() { return (  <button className="square" onClick={() => alert('click')}> {this.props.value} </button> ); } }
请注意onClick={() => alert('click')},我们将函数作为onClickprop 传递它只在点击后触发。忘记() =>和写入onClick={alert('click')}是一个常见的错误,每次组件重新渲染时都会触发警报。

下一步,我们希望Square组件“记住”它被点击,并用“X”标记填充它。为了“记住”事物,组件使用状态

React组件可以通过this.state在其构造函数中设置来具有状态this.state应被视为对其定义的React组件的私有。让我们存储Square的当前值this.state,并在单击Square时更改它。

首先,我们将在类中添加一个构造函数来初始化状态:

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

JavaScript类中,您需要super在定义子类的构造函数时始终调用所有具有a的React组件类都constructor应该通过super(props)调用启动它

现在我们将更改Square的render方法,以便在单击时显示当前状态的值:

  • 更换this.props.valuethis.state.value的内部<button>标签。
  • () => alert()() => this.setState({value: 'X'})替换事件处理程序
  • classNameonClick道具放在不同的线上以提高可读性。

在这些更改之后,<button>Square render方法返回标记如下所示:

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

通过使用Square 方法中this.setStateonClick处理程序调用render,我们告诉React在<button>单击它时重新呈现该Square 更新后,Square this.state.value将会是'X',所以我们将X在游戏板上看到如果你点击任何Square,X就会显示出来。

当您调用setState组件时,React也会自动更新其中的子组件。

class Board extends React.Component {  constructor(props) {  super(props);  this.state = {  squares: Array(9).fill(null),  };  } renderSquare(i) { return <Square value={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> ); } }

为了便于阅读,我们将返回的元素拆分为多行,并添加了括号,以便JavaScript不会在return我们的代码之后插入分号

现在我们传递了从Board到Square的两个道具:valueonClickonClick道具是点击后Square可以调用一个函数。我们将对Square进行以下更改:

  • 更换this.state.valuethis.props.value在Square的render方法
  • 更换this.setState()this.props.onClick()在Square的render方法
  • 删除constructorSquare,因为Square不再跟踪游戏的状态

完成这些更改后,Square组件如下所示:

class Square extends React.Component {  render() { return ( <button className="square"  onClick={() => this.props.onClick()} >  {this.props.value} </button> ); } }

单击Square时,将onClick调用Board提供功能。以下是对如何实现这一目标的回顾:

  1. onClick内置DOM <button>组件prop 告诉React设置一个click事件监听器。
  2. 单击该按钮时,React将调用onClickSquare render()方法中定义事件处理程序
  3. 此事件处理程序调用this.props.onClick()Square的onClick支柱由董事会指定。
  4. 自从董事会转移onClick={() => this.handleClick(i)}到Square后,Square this.handleClick(i)点击时调用
  5. 我们尚未定义该handleClick()方法,因此我们的代码崩溃了。

注意

DOM <button>元素的onClick属性对React具有特殊含义,因为它是一个内置组件。对于像Square这样的自定义组件,命名取决于您。我们可以用不同onClickhandleClick方式命名Square的prop或Board的方法。但是,在React中,使用on[Event]道具名称来表示事件和handle[Event]处理事件的方法是一种惯例

当我们尝试点击Square时,我们应该会收到错误,因为我们还没有定义handleClick我们现在将添加handleClick到Board类:

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

此时查看完整代码

在这些变化之后,我们再次能够点击Squares来填充它们。但是,现在状态存储在Board组件中而不是单个Square组件中。当Board的状态发生变化时,Square组件会自动重新渲染。保持Board组件中所有方块的状态将允许它在将来确定胜利者。

由于Square组件不再维持状态,Square组件从Board组件接收值,并在单击它们时通知Board组件。在React术语中,Square组件现在是受控组件董事会完全控制他们。

请注意handleClick,我们调用.slice()如何创建squares要修改数组副本而不是修改现有数组。我们将squares在下一节中解释为什么要创建数组的副本

为什么不变性很重要

在前面的代码示例中,我们建议您使用.slice()运算符创建squares要修改数组副本,而不是修改现有数组。我们现在将讨论不变性以及为什么不可变性对于学习很重要。

通常有两种改变数据的方法。第一种方法是将变异通过直接更改数据的值的数据。第二种方法是用具有所需更改的新副本替换数据。

数据随变异而变化

var player = {score: 1, name: 'Jeff'}; player.score = 2; // Now player is {score: 2, name: 'Jeff'}

没有变异的数据变化

var player = {score: 1, name: 'Jeff'}; var newPlayer = Object.assign({}, player, {score: 2}); // Now player is unchanged, but newPlayer is {score: 2, name: 'Jeff'} // Or if you are using object spread syntax proposal, you can write: // var newPlayer = {...player, score: 2};

最终结果是相同的,但通过不直接改变(或改变基础数据),我们获得了下面描述的几个好处。

复杂的功能变得简单

不可变性使复杂功能更容易实现。在本教程的后面,我们将实现一个“时间旅行”功能,允许我们查看井字游戏的历史记录并“跳回”以前的动作。此功能并非特定于游戏 - 撤消和重做某些操作的能力是应用程序中的常见要求。避免直接数据突变可以让我们保留游戏历史的先前版本,并在以后重复使用。

检测变化

检测可变对象的变化很困难,因为它们是直接修改的。此检测需要将可变对象与其自身的先前副本和要遍历的整个对象树进行比较。

检测不可变对象中的更改要容易得多。如果被引用的不可变对象与前一个不同,则该对象已更改。

确定何时在React中重新渲染

不变性的主要好处是它可以帮助您在React中构建纯组件不可变数据可以很容易地确定是否已经进行了更改,这有助于确定组件何时需要重新呈现。

通过阅读优化性能,您可以了解有关shouldComponentUpdate()构建纯组件更多信息以及如何构建纯组件

功能组件

我们现在将Square更改为功能组件

在React中,函数组件是编写仅包含render方法且没有自己状态的组件的更简单方法React.Component我们可以编写一个props作为输入并返回应该呈现的内容的函数,而不是定义一个扩展的类与类相比,函数组件编写起来不那么繁琐,并且可以用这种方式表达许多组件。

用这个函数替换Square类:

function Square(props) { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); }

我们已经改变了this.propsprops出现两次。

此时查看完整代码

注意

当我们将Square修改为一个函数组件时,我们也改为onClick={() => this.props.onClick()}更短onClick={props.onClick}(注意两边没有括号)。在类中,我们使用箭头函数来访问正确的this值,但在函数组件中我们不需要担心this

轮流

我们现在需要在我们的井字游戏中修复一个明显的缺陷:无法在棋盘上标记“O”。

我们默认将第一步设置为“X”。我们可以通过修改Board构造函数中的初始状态来设置此默认值:

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

每次玩家移动时,xIsNext(一个布尔值)将被翻转以确定哪个玩家下一个并且游戏的状态将被保存。我们将更新Board的handleClick功能以翻转以下值xIsNext

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

通过这种改变,“X”和“O”可以轮流进行。我们还要更改Board中的“status”文本,render以便显示哪个玩家有下一个回合:

  render() {
 const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); return ( // the rest has not changed

应用这些更改后,您应该拥有此Board组件:

class Board extends React.Component { constructor(props) { super(props); this.state = { squares: Array(9).fill(null),  xIsNext: true, }; } handleClick(i) {  const squares = this.state.squares.slice();  squares[i] = this.state.xIsNext ? 'X' : 'O';  this.setState({  squares: squares,  xIsNext: !this.state.xIsNext,  }); } renderSquare(i) { return ( <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} /> ); } render() {  const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); 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> ); } }

此时查看完整代码

宣布获胜者

现在我们展示下一个玩家的转弯,我们还应该展示何时赢得比赛并且没有更多的转弯。我们可以通过将此帮助函数添加到文件末尾来确定获胜者:

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

我们将调用calculateWinner(squares)董事会的render职能来检查一名球员是否赢了。如果玩家获胜,我们可以显示诸如“Winner:X”或“Winner:O”之类的文本。我们将使用以下代码替换statusBoard render函数中声明

  render() {
 const winner = calculateWinner(this.state.squares);  let status;  if (winner) {  status = 'Winner: ' + winner;  } else {  status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');  } return ( // the rest has not changed

我们现在可以handleClick通过忽略点击来改变委员会的功能,如果有人赢了比赛或者Square已经填满了:

  handleClick(i) {
    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, }); }

































猜你喜欢

转载自www.cnblogs.com/zhouyideboke/p/9753930.html