使用两个循环来渲染出棋盘的格子,而不是在代码里写死(hardcode)
这个关键点是在于循环,react的循环是和vue不一样的, veu中的循环是直接写在节点上,但是react的循环,是通过数组遍历的方法,先遍历出虚拟的Dom节点,然后通过render函数挂载到外层节点上
//使用 Javascript 中的 map() 方法来遍历 numbers 数组。将数组中的每个元素变成 <li> 标签,
//最后我们将得到的数组赋值给 listItems:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
//把整个 listItems 插入到 <ul> 元素中,然后渲染进 DOM
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
key
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。在列表循环中,vue和react一样,都需要为元素绑定一个特定的key,用来进行一系列的判定操作。但是react的key放的地方有点不一样。
这是在 render 中定义的两个数组,row代表的是三行,Cell代表的是每个单元格。通过循环来将棋盘渲染出来。 renderSquare 这个方法就是用来渲染单元格的,可以看到,我们需要双层循环,第一行需要有0,1,2三个单元格,以此类推。
react的渲染格式其实是我比较写不习惯的,因为他的操作都需要包裹在一个 { } 内部,箭头函数的写法也和平常的不一样,=> 之后,不是 { } 作用域,也没有 return 返回。
let row = [0, 1, 2];
let Cell = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
];
let checkerboard = row.map((item, index) => (
<div className="board-row" key={index}>
{Cell.map((item1, index1) =>
index === index1
? item1.map((item2, index) => this.renderSquare(item2))
: ""
)}
</div>
));
从代码里面可以看到,我做了两次循环,其实这个时候,棋盘就已经渲染出来了,但是控制台会报错:Each child in a list should have a unique "key" prop
这是因为,我只是在第一层循环的时候,添加了 key ,内部第二层循环的时候,是没有添加 key 的,所以会报错。
但是仔细看第二层循环,这里其实是调用的一个方法,这里并没有节点,那这个 key 绑定到哪里呢?
renderSquare() 渲染函数 :key绑定在渲染函数的节点上。
//渲染函数不变
renderSquare(i) {
return (
/*
因为 state 是每隔组件私有的数据,不能直接通过 子组件 Square 来改变父组件的 state
所以,我们可以在 子组件被点击的时候,传递一个事件,通过事件传递到父组件,进而改变父组件的数据
相当于 Vue 的父子组件传值,父传子传Props,子传父,通过emit() 传递事件
这样我们就在父组件内部向子组件传递了两个 prop,一个是当前 单元格的值,另一个是一个 onclick 事件
*/
<Square
key={i}
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
如果不调用函数渲染的话,也可以直接写在 map 函数内部
let checkerboard = row.map((item, index) => (
<div className="board-row" key={index}>
{Cell.map((item1, index1) =>
index === index1
? item1.map((item2, index) => (
<Square
key={item2}
value={this.props.squares[item2]}
onClick={() => this.props.onClick(item2)}
/>
))
: ""
)}
</div>
));
总结:在 map 的时候,节点上都需要添加 key ,且元素的 key 只有放在就近的数组上下文中才有意义。