Reputation: 303
I'm following the React Js tutorial from the official site which helps us build a tic-tac-toe game. The square boxes are created by hardcoding all the squares as follows:
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>
);
}
I managed to shorten the code by using a for loop as shown below:
render(){
let sqrRen = [];
for(let i = 0; i < 9; i=i+3){
sqrRen.push(<div className = "board-row">
{this.renderSquare(0+i)}
{this.renderSquare(1+i)}
{this.renderSquare(2+i)}
</div>);
}
return (
<div>
{sqrRen}
</div>
);
}
But I also want to generate the squares in each row using another for loop as follows:
render(){
let sqrRen = [];
for(let i = 0; i < 9; i=i+3){
sqrRen.push(<div className = "board-row">
{
for(let j=0;j<3;j++){
this.renderSquare(j+i)
}
}
</div>);
}
return (
<div>
{sqrRen}
</div>
);
}
but this is not working. I get the following error:
Any suggestions on how to go about using two for loops?
Upvotes: 8
Views: 4596
Reputation: 741
Here's a solution using functional components:
const rowCount = 3, colCount = 3;
return (
<>
<div className="status">{status}</div>
<div>
{[...new Array(rowCount)].map((x, rowIndex) => {
return (
<div className="board-row" key={rowIndex}>
{[...new Array(colCount)].map((y, colIndex) => {
const position = rowIndex * colCount + colIndex
return <Square key={position} value={squares[position]} onSquareClick={() => handleClick(position)} />
}
)}
</div>
)
})
}
</div>
Upvotes: 0
Reputation: 11
This is my attempt at this tutorial. Some of the other solutions generated a warning that no key was present for each of the squares. I attempted to fix that here. I also split out rendering of the row.
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)
key={i}
/>
);
}
renderRow(rowIndex) {
return (
<div key={rowIndex} className="board-row">
{[...new Array(3).keys()].map(colIndex => {
return this.renderSquare(rowIndex * 3 + colIndex)
})}
</div>
);
}
render() {
return (
<div>
{[...new Array(3).keys()].map(rowIndex => {
return this.renderRow(rowIndex)
})}
</div>
);
}
}
Upvotes: 0
Reputation: 531
Here's my attempt at this react tutorial, in case it helps someone. I would appreciate if someone could suggest another method and the logic applied as I am pretty sure there are other better methods than the one below that I am suggesting.
class Board extends React.Component {
constructor(props) {
super(props);
}
renderSquare(i) {
return (
<Square
key={i}
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
active={this.props.currentCell === i}
/>
);
}
renderBoard = () => {
let squareRows = [];
let count = 0;
for (let x = 0; x < 3; x++) {
const squareCols = [];
for (let y = 0; y < 3; y++) {
squareCols.push(this.renderSquare(count));
count++;
}
squareRows.push(squareCols);
}
return squareRows;
};
render() {
return (
<div>
{this.renderBoard().map((squareRow, index) => {
return (
<div key={index} className="board-row">
{squareRow.map((square) => square)}
</div>
);
})}
</div>
);
}
}
Upvotes: 0
Reputation:
You have to create the element structure first, then pass it to the container element.
class Board extends React.Component {
renderSquare(i) {
return (
<Square
//give each square its unique key
key={i}
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
fillBoard=()=>{
//make an empty array for storing the rows
let rows =[];
//a function scoped counter for keeping track of current square
let counter = 0;
for(let x = 0; x<3; x++){
//create an array to store each child of board-row (square)
let rowSquares = [];
for(let y = 0;y<3;y++){
//fill the second array with the children (squares)
rowSquares.push(this.renderSquare(counter));
//increase function scoped variable to track current square
counter++;
}
//push each row object into the first array with the children array included
rows.push(<div className="board-row" key={x}>{rowSquares}</div>)
}
//return the rows array
return rows;
}
render() {
//render the parent container div with its child the rows array
return(<div>{this.fillBoard()}</div>)
}}
Double for loops can be confusing on their own, adding react to the equation doesn't help. In plain English what you are doing is saying:
Upvotes: 1
Reputation: 31
Since I stuck at this additional problem for hours yesterday and finally arrived at this "two For loops" solution, let me share it here for upcoming react learners.
class Board extends React.Component {
renderSquare(i) {
return (
<Square
key={i} //prevent warning
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
render() {
const row = [];
let k = 0; //key
for (let i = 0; i < 3; i++) {
const col = [];
for (let j = 0; j < 3; j++) {
col.push(this.renderSquare(3*i+j)); //push argument without {}, <>
k++;
}
row.push(<div key={k} className="board-row">{col}</div>);
}
return (
<div>
{row}
</div>
);
}
}
The most important part is to figure out how to assemble JSX syntax with push. Here "this.renderSquare(3*i+j)" will just return the element, so that's what we want. I personally think this is better than Array.prototype.map solutions.
Upvotes: 3
Reputation: 61
Since I was also looking for this, the accepted answer pointed me to the right direction. Though I made a slightly different implementation without lodash.
render() {
const rowCount = 3, colCount = 3;
return (
<div>
{[...new Array(rowCount)].map((x, rowIndex) => {
return (
<div className="board-row" key={rowIndex}>
{[...new Array(colCount)].map((y, colIndex) => this.renderSquare(rowIndex*colCount + colIndex) )}
</div>
)
})
}
</div>
);
}
Upvotes: 5
Reputation: 1006
You don't need to use the usual for
loops as they look very messy. Instead, you should utilize the new ES6 features and syntax to achieve a cleaner and more understandable approach.
The solution below renders a 3x3 tic-tac-toe board just fine:
import chunk from 'lodash/chunk';
const styles = { width: '40px', height: '40px', float: 'left', textAlign: 'center' };
return (
<div className="tic-tac-toe-container">
{chunk(new Array(9).fill(0), 3).map((item, itemIndex) => {
return (
<div key={itemIndex} className="row">
{item.map(col => <div className="col" style={styles}>COL</div>)}
</div>
)
})
}
</div>
);
Upvotes: 4
Reputation: 1
What's the error you're getting in the console? My first guess is you have not defined j in your loop. Try
let j=0
Upvotes: -1