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

Posted

技术标签:

【中文标题】React - 使用循环创建嵌套组件【英文标题】:React - Create nested components with loops 【发布时间】:2017-05-30 18:11:12 【问题描述】:

我对 React 有一点问题。我无法使用 for 循环创建嵌套组件。我想要做的是创建一个表格的 9 个单元格,然后创建 3 行,每行 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>

我想澄清一下,我确信与该组件(板)交互的其余代码没有问题。

我是新手,如果有人可以帮助我,我会非常感激。 对不起我的英语不好

编辑1: 以下 ma​​rklew 示例我应该能够做这样的事情

    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
     />

我把 X 同时淹没在 3 个牢房里

编辑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) 函数,我也想要另一个循环。

【问题讨论】:

【参考方案1】:

要在 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 属性,正如 here 所指出的那样。

另外,如果您只想在渲染函数中打印行值,您应该替换:

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

与:

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

或者,将forEachpush 替换为map

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

编辑我使用您的代码作为基础创建了一个 9x9 板的小提琴:https://jsfiddle.net/mrlew/cLbyyL27/(您可以单击单元格以选择它)

【讨论】:

@MarianDiaconu 我用一个有效的小提琴更新了我的答案 也许我误解了您提供的链接,但它说应该将键分配给列表项,并且通常没有说明组件数组。【参考方案2】:

我看到你也在做 JS React 教程!这就是我所做的,但我正在努力解决这个问题,因为必须有一种很好的方法来提供这些单独的密钥。

我遇到了与您在将 X 绘制成 3 个正方形时遇到的相同问题,发生这种情况的原因是当您渲染正方形时,一些“i”被重复了。因此,例如,有多个“i”为 2 的 Square(至少在我的问题中是这种情况)。

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

首先,我们需要找出这些是如何关联的,我们可以将它们呈现为行!因此,在您的示例中,您有 index1 和 index2,我假设它们将引用 Board 中 Square 的 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”值与之关联。

设置循环后,我使用this SO 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;

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

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

【讨论】:

【参考方案3】:

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

附:我也在新的React Tutorial 中接受挑战。 =p

【讨论】:

【参考方案4】:

Live demo on codepen

render() 函数中的嵌套循环 (用 cmets 解释:)

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

【讨论】:

【参考方案5】:

我假设您正在查看 React 教程示例。正如说明中明确指出的那样,这个想法是创建两个循环,而不仅仅是一个。

本例中的第一个循环创建了 3 行。第二个嵌套循环创建 3 个必要的列,通过 renderSquare 函数为每个位置返回一个 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>
  );

【讨论】:

你能补充一些解释吗?谢谢。 抱歉,@Sanyash。让我知道是否需要进一步说明。【参考方案6】:

这是我的解决方案。它可能会有所帮助。

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

【讨论】:

【参考方案7】:

这个 sn-p 使用 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>;

【讨论】:

【参考方案8】:

您正在将函数推送到 board 数组。如果要渲染它们,则必须调用这些函数,例如

board[0]()

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

【讨论】:

我不明白这如何解决我的问题 -> 我收到一个错误:board is not a function 哦,也许现在我明白了,当我执行 rows.push(() => 你说我在数组中推送一个函数而不是一个反应元素?我想要推送元素我怎么能这样做?【参考方案9】:

我也在编写 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 组件的键,作为第二个参数。

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

【讨论】:

【参考方案10】:

这应该可以解决您面临的问题。

这甚至为实习生 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>
    );
  

【讨论】:

【参考方案11】:

第一种解决方案:

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

第二种解决方案:

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

正方形:

import React from 'react';

export default
function Square(props) 
  console.log('square render')
  return (
    <button className="square" onClick=props.onClick>
      props.value
    </button>
  );

【讨论】:

【参考方案12】:
class Board extends React.Component 
  renderSquare(i) 
    return (<Square key=i value=this.props.squares[i] onClick=() => this.props.onClick(i) />);
  

  render() 
    let board = [];
    for(let x = 0; x<3; x++)
      let lis =[];
      for(let y = 0; y<3; y++)
        lis.push(this.renderSquare(3*x + y));
      
      board.push(<div key=x>lis</div>)
    
    return (
      <div>
        board
      </div>
    );
  

【讨论】:

通常只使用代码的答案是不受欢迎的。您应该在答案中添加描述:)【参考方案13】:

我的解决办法是:

class Board extends React.Component 

    //range is a Sequence generator function
    //Array.from(arrayLike[, mapFn [, thisArg]]).
    //arrayLike - An array-like or iterable object to convert to an array
    //mapFn - Map function to call on every element of the array
    //thisArg - Value to use as this when executing mapFn
    //range(0, 4, 1);  => [0, 1, 2, 3, 4] 

    range = (start, stop, step) => Array.from(
         length: (stop - start)/step +1 , 
        (_, i) => start + (i * step)
    );

    renderSquare(i) 
        return <Square 
            key=i
            value=this.props.squares[i] 
            onClickChange=() => this.props.onClickChange(i) 
            />;
    

    render() 
    let row = 3;
    let col = 3;
    return (
        <div>
            
                //This generates an Array of [0, 3, 6] if row = 3 and col = 3
                // n is the element and i is the index of the element, i starts at 0
                this.range(0, row * col - 1, col).map( (n, i) => 
                    return (
                        <div key=i className="board-row">
                            
                                this.range(n, (i + 1) * col - 1, 1).map( (n, i) => 
                                return this.renderSquare(n)
                                )
                            
                        </div>
                    )
                )
            
        </div>
    );

【讨论】:

【参考方案14】:

从教程中更改了 Board 的代码以反映为

class Board extends React.Component 
  constructor(props) 
    super(props);
    this.rows = Array(3).fill(null);
  
  
  renderSquare(i) 
    return (<Square
      key=i 
      value=this.props.squares[i]
      onClick=() => this.props.onClick(i)
    />);
  
  
  renderRow(row) 
    return (
      <div key=row className="board-row">
        this.rows.map((x, col) => this.renderSquare(3*row+col))
      </div>
    );
  

  render() 
    return (
      <div>
        this.rows.map((x, rowIndex) => this.renderRow(rowIndex))
      </div>
    );
  

【讨论】:

【参考方案15】:

上面提供数组的答案,破坏了循环的目的;这并不漂亮,但话说回来,React 与 JSX 也不是:

class Board extends React.Component 
    
    render() 
        let board=[];
        for(let row=0;row<9;row+=3) 
            let cols=[];
            for(let col=0;col<3;col++) 
                let i=col+row;
                cols.push(<Square value=this.props.squares[i] onClick=() => this.props.onClick(i)/>)
            
            
            board.push(
                <div className="board-row">
                    cols
                </div>
            );
           

        return (
            <div>
                board
            </div>
        );
    

【讨论】:

以上是关于React - 使用循环创建嵌套组件的主要内容,如果未能解决你的问题,请参考以下文章

2017.12.07 React组件嵌套以及for循环渲染子组件

React 组件循环更新 (GraphQL)

如何为 React Native 嵌套组件创建 Typescript 定义

如何在 React 路由器中嵌套动态页面路由组件?

React入门---组件嵌套-5

使用 React JS 的组件“循环”