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?
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?
Share Improve this question edited Nov 4, 2018 at 5:26 sketchthat 2,6882 gold badges15 silver badges22 bronze badges asked Aug 8, 2017 at 15:51 Aanchal AdhikariAanchal Adhikari 3034 silver badges12 bronze badges8 Answers
Reset to default 5Since 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>
);
}
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>
);
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.
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:
- Lets make an array to store our row objects
- Make a for loop to iterate how ever many rows we need.
- Now that we are inside of our first loop, lets make an empty array specific to this iteration of our first row object, where we will store its children.
- Create a second for loop to iterate over each child we want to add to this row.
- Now that the second loop has added each square to the row parent and is done, we move back into the FIRST loop to finish up by simply adding this completed row object and its children (the squares) to the function scoped array for storing the rows we made before the loops.
- When we want to render, we just call that function and put the returned object (our nifty list of rows and squares tree array) inside of our container div.
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>
);
}
}
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>
);
}
}
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>
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