React入门(三)

  React入门(三)


  本节介绍React组件的一些特性。


  一、组件的属性


  组件的标签和HTML标签相近,所以也可以有自己的属性,例如:<Square value='0' />。


  父组件可以在使用子组件时,通过属性,向子组件传递数据。


  例如要显示如下的游戏区:




  可以这样使用我们的组件。

  父组件代码:


class Board extends React.Component {
  renderSquare(i) {
    return <Square value={i} />; // 父组件为子组件设置属性
  }

  子组件代码:


class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {this.props.value} // 子组件使用属性
      </button>
    );
  }
}

  在子组件中,可以使用this.props.[属性名]的方式,从props集合中得到指定属性的值。


  二、组件的状态


  属性可以在组件初始化时传入,通常用于父组件和子组件之间的数据通信。


  但属性值的变化并不会引起组件的界面变化,状态提供了一种另一种可能。组件的状态变化会导致组件的界面刷新。


  现在我们给Square类加一个状态,“{ value: null }”,当点击格子时,把状态改成“{ value: 'X' }”,由于状态变化会刷新组件,因此格子上的文字变成了“X”。

  代码如下:

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

  这里,我们在类的构造方法中为状态赋初始值,然后在点击事件添加代码:this.setState({value: 'X'}),setState()方法可以改变状态。

  () => 是箭头函数,是ES6新增的语法,相当于:function() { this.setState({value: 'X'}); }

  React又回归了传统的事件处理代码的编写模式,用onClick=绑定事件处理代码,要注意的是事件使用驼峰命名法(例如onClick,而非onclick)。

  onClick=后面是一个JS函数,所以用{}包围。


  三、将状态放在上一级


  在上例中,我们把状态放在每一个格子中,但这样做不利于统计。

  所以我们把状态移到了上一级中,如下:

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

  现在,我们用一个数组表示九宫格中九个格子的状态,数组中的数据类似于:

[
  'O', null, 'X',
  'X', 'X', 'O',
  'O', null, null,
]

  现在出现一个新的问题:状态放在上一级,当点击格子时,需要修改上一级的状态。

  但数据通常是私有的,不应被外界访问,那应该怎么办呢?

  我们可以这样:当点击格子时,点击事件执行的是定义在上一级中的回调函数。


  代码如下:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

class Square extends React.Component {
    render() {
        return (
            <button className="square" onClick={() => this.props.onClick()}> // 点击事件的回调函数使用属性onClick的值
                {this.props.value}
            </button>
        );
    }
}

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

    handleClick(i) {
        const data = this.state.data.slice();
        data[i] = 'X';
        this.setState({ data: data }); // 改变状态,此时会重新调用render()方法
    }

    renderSquare(i) {
        return (
            <Square
                value={this.state.data[i]} // 添加属性value,其值为状态数组中的值
                onClick={() => this.handleClick(i)} // 添加属性onClick,其值为内部函数handleClick
            />
        );
    }

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


  上述代码中,可能有一处不好理解,就是改变状态的代码:


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

  这里为什么要用slice()?

  这里主要是因为React强调数据的不变性,它说这样可以更容易跟踪数据的变化,所以最好不要直接修改数据,而是复制数据再修改。

  数组的slice()方法返回数组的一个副本。


猜你喜欢

转载自blog.csdn.net/hanhf/article/details/80062561
今日推荐