前言
接着前面的空格子棋 加上其他功能变成真正的井字棋
数字显示添加
传值 prop 方式
修改 Board 类的renderSquare函数
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
}
value = {i} 就是传值方式
同样的修改 Square 的渲染函数 接收参数并且 显示出来
class Square extends React.Component {
render() {
return (
<button className="square">
{this.props.value}
</button>
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nGOqHDAQ-1617353498608)(en-resource://database/2034:1)]
给组件添加交互功能
Square 组件中 render() 方法的返回值中的 button 标签修改为如下内容:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={function() { alert('click'); }}> {this.props.value}
</button>
);
}}
使用箭头函数代替 onClick={() => alert('click')}
使用state记录状态
可以通过在 React 组件的构造函数中设置 this.state 来初始化 state。this.state 应该被视为一个组件的私有属性。我们在 this.state 中存储当前每个方格(Square)的值,并且在每次方格被点击的时候改变这个值。
添加构造函数 初始化state
constructor(props) {
super(props);
this.state = {
value: null,
};
}
现在,我们来修改一下 Square 组件的 render 方法,这样,每当方格被点击的时候,就可以显示当前 state 的值了:
1.在 标签中,把 this.props.value 替换为 this.state.value。
2.将 onClick={…} 事件监听函数替换为 onClick={() => this.setState({value: ‘X’})}。
为了更好的可读性,将 className 和 onClick 的 prop 分两行书写。
render() {
return (
<button
className="square"
onClick={() => this.setState({value: 'X'})}
>
{this.state.value}
</button>
);
}
setState 就是设置state的值
其中value 就是参数名
this.state.value 就是取值
使用子布局存储状态
给子布局添加Board构造函数 参数
使用一个arry储存状态
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]} />;
}
接下来,我们要修改一下 Square 的点击事件监听函数。Board 组件当前维护了那些已经被填充了的方格。我们需要想办法让 Square 去更新 Board 的 state。由于 state 对于每个组件来说是私有的,因此我们不能直接通过 Square 来更新 Board 的 state。
相反,从 Board 组件向 Square 组件传递一个函数,当 Square 被点击的时候,这个函数就会被调用。接着,我们将 Board 组件的 renderSquare 方法改写为如下效果:
在修改这个函数 添加一个handleClick函数 就是为了将点击事件从board传递到Square。
也就是说点击Square的时候同时触发了Board的函数
handleClick 现在还没有定义
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>
);
}
参数函数接收
现在我们从 Board 组件向 Square 组件中传递两个 props 参数:value 和 onClick。onClick prop 是一个 Square 组件点击事件监听函数。接下来,我们需要修改 Square 的代码:
1.将 Square 组件的 render 方法中的 this.state.value 替换为 this.props.value 。
2.将 Square 组件的 render 方法中的 this.setState() 替换为 this.props.onClick() 。
删掉 Square 组件中的构造函数 constructor,因为该组件不需要再保存游戏的 state。
进行上述修改之后,代码会变成下面这样:
class Square extends React.Component {
render() {
return (
<button
className="square"
onClick={() => this.props.onClick()}
>
{this.props.value}
</button>
);
}
}
props就是用来取值的:
props.value 取值value值
prop.onClick 是一个 Square 组件点击事件监听函数 也就是将点击事件传递下去
看清楚了发送和接收的关系对应
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
onClick={() => this.props.onClick()
{this.props.value}
handleClick函数定义
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
整体流程解析
每一个 Square 被点击时,Board 提供的 onClick 函数就会触发。我们回顾一下这是怎么实现的:
- 向 DOM 内置元素 添加 onClick prop,让 React 开启对点击事件的监听。
当 button 被点击时,React 会调用 Square 组件的 render() 方法中的 onClick 事件处理函数。 - 事件处理函数触发了传入其中的 this.props.onClick() 方法。这个方法是由 Board 传递给 Square 的。
- 由于 Board 把 onClick={() => this.handleClick(i)} 传递给了 Square,所以当 Square 中的事件处理函数触发时,其实就是触发的 Board 当中的 this.handleClick(i) 方法。
- 现在我们还尚未定义 handleClick() 方法,所以代码还不能正常工作。如果此时点击 Square,你会在屏幕上看到红色的错误提示,提示内容为:“this.handleClick is not a function”。
简单说明就是:
点击Square这个事件会放到Board的renderSquare函数里面去处理
而renderSquare函数的点击事件就是handleClick函数 也就是处理点击事件的意思
handleClick函数则将参数Board state里的参数
arrayObject.slice(start,end) 返回值 返回一个新的数组
并设置点击的数组位置的变量为X,同时将新的数组返回给Board的state
因为点击事件改变了数组的值,同时传递Square的值也是数组的值。
这就等同于点击Square的时候改变了Square的值。
但是其实是在父布局完成了所有操作,只是交出来了点击权限。
React 术语中,我们把目前的 Square 组件称做“受控组件”。
在这种情况下,Board 组件完全控制了 Square 组件。
使用arrayObject.slice创建副本
在上一节内容当中,我们通过使用 .slice() 方法创建了数组的一个副本,而不是直接修改现有的数组。接下来我们来学习不可变性以及不可变性的重要性。
一般来说,有两种改变数据的方式。
第一种方式是直接修改变量的值,第二种方式是使用新的一份数据替换旧数据。
使用arrayObject.slice创建副本 修改之后替换原来的list。
而不是之间修改之前的list