React - 使用循环创建嵌套组件

26

我在使用React时遇到了一点问题。我无法使用for循环创建嵌套组件。我的意图是创建一个9个单元格的表,并为每行创建3个单元格,然后将这3行安装在一起,创建一个9x9的棋盘。

假设我想要像这样得到某些东西,但要使用循环:

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

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>
    );        
}

我搜寻了数小时其他人的问题,我认为我的代码几乎正确,但它没有呈现出我想要的。我只得到了一个白色页面。

这是我的代码:

class Board extends React.Component { 

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

createCells(i){
    if(i%3){return;}
    var index = this.fillN(Array(i)); //index=[0,1,2,3,4,5,6,7,8]
    var cells = [];
    index.forEach(function(i){
        cells.push(() => {
            return(
                <div>
                    {this.renderSquare(i)}
                </div>
            );
        });
    });
    return cells;
}

createRows(cells){
    var index = this.fillMod3(Array(3)); //index=[0,3,6]
    var rows = []
    index.forEach(function(i){
        rows.push(() => {
            return(
                <div>
                    {cells[i]}
                    {cells[i+1]}
                    {cells[i+2]}
                </div>
            );
        });
    });
    return rows;
}

render(){    
    var cells = this.createCells(9);
    var rows = this.createRows(cells);
    var board = [];
    var index = this.fillN(Array(1));

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

    return(
        <div>
            {board[0]}
        </div>
    );        
}

我总是在屏幕上看到类似这样的东西:

<Board>
  <div> /*empty*/ </div>
</Board>

我想澄清的是,我确信该组件(Board)与其交互的其他代码没有问题。

我是 React 新手,如果有人能帮我,我将非常感激。 对不起,我的英语很差。

编辑1: 根据 marklew 的示例,我应该可以做出类似这样的东西

    render(){   
    var index1 = this.fillN(Array(3)); //index1=[0,1,2]
    var index2 = this.fillN(Array(3)); //index2=[0,1,2]

    return(
        <div>
            {index1.map((e1,i1) => {
                return(
                    <div key={i1} className="board-row">
                        {index2.map((e2, i2) => {
                            return(
                                <p key={i2+10}>
                                    {this.renderSquare(i2)}
                                </p>
                            )
                        })}
                    </div>
                )    
            })}
        </div>
    );

}

但它不能做我想要的事情。我只获得了一列有9个单元格的表格,而这些单元格是相同的对象。我不明白为什么会这样。(我知道它们是相同的对象,因为我在创建它们时分配了一个 onClick 的句柄函数):

<Board 
     onClick={(i) => this.handleClick(i)} //handleClick just draws a X in the cell
     />

我同时在 3 个单元格中使 X 淹没。

编辑2:

我找到了解决方案:

render(){   
    var index1 = this.fillMod3(Array(3));        

    return(
        <div>
            {index1.map((e,i) => {
                return(
                    <div key={i} className="board-row">
                        {this.renderSquare(e)}
                        {this.renderSquare(e+1)}
                        {this.renderSquare(e+2)}
                    </div>
                )    
            })}
        </div>
    );

}

但这不是我想要的。我想为内部renderSquare(i)函数再加一个循环。

18个回答

26
在JSX中渲染一个元素列表,你可以像这样做:
render() {
    return <div>
        {
            [1,2,3].map ( (n) => {
                return this.renderSquare(n)
            })

        }
    </div>;
}   

只需在JSX中将您的组件数组包装在{}中即可。

稍作澄清,这与以下逻辑相同:

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

请注意,每次呈现组件数组时,必须提供key属性,如此处所述。

另外,如果您想在呈现函数中简单地打印行值,则应替换为:

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

与之搭配:

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

或者,然而,用 map 替换 forEachpush

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

编辑 我以您的代码为基础创建了一个9x9棋盘的演示: https://jsfiddle.net/mrlew/cLbyyL27/ (您可以单击单元格来选择)


@MarianDiaconu 我更新了我的答案,并提供了一个可用的 fiddle。 - mrlew
也许我误解了您提供的链接,但它说键应该分配给列表项,并没有涉及一般组件数组的任何内容。 - tsujin

20

我看到你也在做JS React教程!这是我所做的,但我正在处理这个问题,因为必须有一个好的方法来给每个单独的键。

我遇到了与你相同的问题,即X被绘制成3个正方形,原因是当你渲染正方形时,一些“i”的值被复制了。例如,至少在我的情况下,有多个“i”等于2的正方形。

所以每个方格都有一个索引对吧? [0,1,2,3,4,5,6,7,8]。

首先,我们需要找出它们之间的关系,以便我们可以将它们渲染成行!所以,在你的例子中,你有index1和index2,我想这将参考棋盘上的x和y坐标。重新编写上面的数组,我们得到:[{0,0},{1,0},{2,0},{0,1},{1,1},{2,1},{0,2},{1,2},{2,2}],使用{x,y}格式。我们如何使用这些值(我们会从你的index1和index2获取)来获取我们最初开始使用的值的原始数组[0,1,2,3,4,5,6,7,8]呢?

我所做的是3 * x + y(或在循环中,3 * i + j)。这样,每个正方形都有一个唯一的“i”值与之相关联。

在设置好循环后,我使用了这个 SO帖子来正确返回从另一个函数中创建的元素(在尝试保持代码更清洁的努力下)。

这就是我最终得到的结果,但我需要确保正确设置键,这实际上也是我偶然发现这篇帖子的原因:

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;
}

那么我的Board中的渲染函数应该是这样的:

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

7

在阅读此处和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>
    );
  }

附注:我也在完成新的React教程中的挑战。=p


3

在CodePen上的实时演示

render()函数中嵌套循环(请参见注释解释)

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>
    );  
  }
}

1
这段代码使用 map 类似于我们定义嵌套循环的方式。

const buildBoard = [0, 1, 2].map((row) => {
  return (
    <div className='board-row' key={row}>
      {[0, 1, 2].map((col) => {
        return this.renderSquare(row * 3 + col);
      })}
    </div>
  );
});

return <div id='Board Container'>{buildBoard}</div>;


1
这是我的解决方案。可能会有所帮助。
renderSquare(i) {
    return (
      <Square
        key={i}
        value={this.props.squares[i]}
        onClick={() => this.props.onClick(i)}
      />
    );
  }

  render() {
    
    let rows = [];
    for (let i = 0; i <= 2; i++) {
      let children = []

      for (let j = i * 3; j <= i * 3 + 2; j++) {
        children.push(this.renderSquare(j))
      }

      rows.push(
        <div key={i} className="board-row">
          {children}
        </div>
      )
    }

    return (
      <div>
        {rows}
      </div>
    );
  }

1
我将假设您正在查看React教程示例。正如说明明确指出的那样,这个想法是要创建两个循环,而不仅仅是一个。
此示例中的第一个循环创建了3行。第二个嵌套循环创建了3个必要的列,通过renderSquare函数为每个位置返回一个方块。您可能会注意到,我使用两个循环的索引来正确地分配相应的索引给方块。
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>
  );

请您能否添加一些解释?谢谢。 - sanyassh
抱歉,@Sanyash。如果需要我进一步解释,请告诉我。 - Mateo Marin

0
你正在将函数推送到 board 数组中。如果你想要渲染它们,你需要调用这些函数,例如:
{board[0]()}

我准备了一个涵盖你问题的例子:http://jsbin.com/hofohiy/edit?js,output


我不明白这怎么能解决我的问题 -> 我收到了一个错误消息: {board 不是一个函数}。 - neboduus
哦,也许我现在明白了,当我执行rows.push(() => {时,你是说我将一个函数推入数组而不是React元素?我想要推入元素,我该怎么做? - neboduus

0

我也在学习React教程。感谢您的回复。我找到了这个解决方案:

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>
    );
}

我在renderSquare函数中更改了Square组件的key值,将其作为第二个参数传递。

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

0

这应该解决你面临的问题。

这会为 intern renderSquare(i) 函数添加另一个循环。 它可以实现你想要的,即在井字棋游戏中显示3列3个单元格。

 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>
    );
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接