React - Create nested components with loops

回眸只為那壹抹淺笑 提交于 2019-12-04 03:13:28

To render a list of elements inside JSX, you can do something like that:

render() {
    return <div>
        {
            [1,2,3].map ( (n) => {
                return this.renderSquare(n)
            })

        }
    </div>;
}   

Just wrap your array of components into {} in your JSX.

To clarify a bit, this is the same logic of:

return <div>
    {
        [
            <h1 key="1">Hello 1</h1>,
            <h1 key="2">Hello 2</h1>,
            <h1 key="3">Hello 3</h1>
        ]           
    }
</div>;

Note that everytime you render an array of components, you must provide a key prop, as pointed here.

Also, if you want simply print row value in your render function, you should replace:

index.forEach(function(row){
    board.push(() => {
        return(
            <div>{row}</div>
        );
    });
})

with:

index.forEach( (row, index) => {
    board.push(<div key={index}>{row}</div>)
})

or, yet, replacing forEach and push with map:

board = index.map( (row, index) => <div key={index}>{row}</div> )

EDIT I created a fiddle with a 9x9 board using your code as a base: https://jsfiddle.net/mrlew/cLbyyL27/ (you can click on the cell to select it)

I see you too are doing the JS React tutorial! Here's what I did, but I'm working on this because there has to be a good way to give each of these individual keys.

I ran into the same issue you were running into where the X's were drawn into 3 squares, and the reason that happens is because when you were rendering squares, some of the "i's" were duplicated. So there were multiple Squares with the "i" of 2 for example (at least that was the case in my issue).

So each Square has an index right? [0,1,2,3,4,5,6,7,8].

First we need to find how these are related in a way that we can render them into rows! So, in your example you have index1 and index2, which I assume will refer to the x and y coordinates of the Square in the Board. Re-writing the array above we come to: [{0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {2,1}, {0,2}, {1,2}, {2,2}] using {x,y} format. How can we use these values (which we would get from your index1 and index2 in order to get the original array of values that we started with [0,1,2,3,4,5,6,7,8]?

What I did was 3 * x + y (or in a loop, 3 * i + j). This way each square has a unique "i" value associated with it.

After I set up my loops I used this SO post to correctly return the elements I created from a separate function (in a poor attempt to keep my code cleaner).

This is what I ended up with, but I need to make sure to set the keys correctly which is actually how I stumbled onto this post:

createSquares() {
  let rows = [];
  for(var i = 0; i < 3; i++){
    let squares = [];
    for(var j = 0; j < 3; j++){
      squares.push(this.renderSquare(3*i+j));
    }
    rows.push(<div className="board-row">{squares}</div>);
  }
  return rows;
}

Then my render function in Board looks like this:

render() {
  return (
    <div>
      {this.createSquares()}
    </div>
  );
}

Here is the best I could think of after reading answers here and in Loop inside React JSX :

  render() {
    return (
      <div>
        {
          [...Array(3)].map((_, i) => (
            <div key={i} className="board-row">
              {
                [...Array(3)].map((_, j) => this.renderSquare(3 * i + j))
              }
            </div>
          ))
        }
      </div>
    );
  }

P.S. I'm also doing the challenges in the new React Tutorial. =p

Live demo on codepen

Nested loops in render() function (explanation in comments:)

class Board extends React.Component {
  renderSquare(i) {
    return (
      <Square
        value={this.props.squares[i]}
        key={i}
        onClick={() => this.props.onClick(i)}
      />
    );
  }

  render() {
     var self = this;
    return (
      <div>
      // you can use: [...Array(3)] instead of [1,2,3]
        {[1, 2, 3].map(function(row, rowIdx) { // create rows 
          return (
            <div className="board-row" key={rowIdx}>
              {
              // you can use: [...Array(3)] instead of [1,2,3]
              [1, 2, 3].map((col, colIdx) => { // create columns
                return self.renderSquare((3 * rowIdx) + colIdx); // calculate square index
              })
              }
            </div>
          );
        })}
      </div>
    );  
  }
}

I will assume you are looking at the React Tutorial example.. As the instructions explicitly point out, the idea is to make two loops, not just the one.

The first loop in this example creates the 3 rows. The second nested loop creates the 3 necessary columns, returning a Square for each position through the renderSquare function. You may notice that I use the indexes of both loops to correctly assign the corresponding index to the square.

return (
    <div>
      {[0,1,2].map(i => {
        return (
          <div className="board-row">
            {[0,1,2].map(j => {
              return this.renderSquare(3*i + j)
            })}
          </div>
        );
      })}
    </div>
  );

You are pushing functions to the board array. If you want render them, you have to call those functions like

{board[0]()}

I prepared an example that covers your problem: http://jsbin.com/hofohiy/edit?js,output

I'm working on the React tutorial also. Thanks for your responses. I got to this solution:

render() {
    const rows = [];
    for(let i = 0; i<9; i=i+3) {
        const oneRow = [];
        for(let j = i; j < i+3; j++) {
            oneRow.push(this.renderSquare(j, j))
        }
        rows.push(<div className="board-row" key={i + 'someId'}>{oneRow}</div>)
    }
    return (
        <div>
            {rows}
        </div>
    );
}

Where I changed renderSquare to set a key for the Square component, as the second argument.

renderSquare(i, key) {
    return (
        <Square
            value={this.props.squares[i]}
            onClick={() => this.props.onClick(i)}
            key={key}
        />
    );
}

This should resolve the problem your facing.

This adds another loop even for the intern renderSquare(i) function. It does what you want i.e. display 3 columns with 3 cells for the tic tac toe game.

 render() {
    let count = 0;
    const index = [1, 2, 3];
    return (
      <div>
        {index.map((v, i) => {
          return (
            <div key={i} className="board-row">
              {index.map((v2, i2) => {
                return this.renderSquare(count++);
              })}
            </div>
          );
        })}
      </div>
    );
  }
Fatih Ertuğral

The first solution:

import React from 'react';
import Square from './Square';

export default
class Board extends React.Component {
  render() {
    const board2 = [];

    for (let i = 0; i < 3; i++) {
      const s = [];
      for (let k = 0; k < 3; k++) {
        s[k] = <Square
          key     ={3*i+k}
          value   ={this.props.squares[3*i+k]}
          onClick ={()=>this.props.onClick(3*i+k)}
          />;
      }
      board2[i] = <div className="board-row" key={i}>{s}</div>;
    }
    ///////////////////////////////////////
    return (
      <div>{board2}</div>
    );
  }
}

The second solution:

import React from 'react';
import Square from './Square';

export default
class Board extends React.Component {
  render() {    
    let board   =Array(3).fill(null);
    let square  =Array(3).fill(null);

    const x = board.map((b,bIndex)=>{
      return<div className="board-row" key={bIndex}>
        {
          square.map((s, sIndex)=>{
            return <Square
            key     ={3*bIndex+sIndex}
            value   ={this.props.squares[3*bIndex+sIndex]}
            onClick ={()=>this.props.onClick(3*bIndex+sIndex)}
            />;
          })
        }
      </div>;
    });
    ///////////////////////////////////////
    return (
      <div>{x}</div>
    );
  }
}

Square:

import React from 'react';

export default
function Square(props) {
  console.log('square render')
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!