0

I was following the tutorial on how to build with React with the TIC TAC TOE example, and I found it very interesting.

At the end of the tutorial, I found one of the following challenges:

Rewrite Board to use two loops to make the squares instead of hard coding them.

This is the original rendermethod

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 first did it with just 1 loop and everything was working great

render() {
    let col_count = 3;
    let row_count = 3;

    let rows = [];
    for (var i = 0; i < row_count; i++) {
        rows.push(
            <div key={i} className="board-row">
                {this.renderSquare(i*col_count + 0)}
                {this.renderSquare(i*col_count + 1)}
                {this.renderSquare(i*col_count + 2)}
            </div>
        );
    }

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

    );
}

But then I tried to code it with 2 loops:

render() {
    let col_count = 3;
    let row_count = 3;

    let rows = [];
    for (var i = 0; i < row_count; i++) {
        let cols = [];
        for (var j = 0; j < col_count; j++) {
            cols.push(this.renderSquare(i*col_count + j));
        }

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

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

    );
}

And I started getting the famous "keys" error on the console:

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of Board.

So now I wonder: What is that I am missing? The rows have still a valid key ID that was working when i was just using one loop. And the columns inside each row come from another render method that has always worked.

Where is the problem with my second implementation? How could I implement it so that I do not get that error?

Update:

In case needed, this is renderSquare

renderSquare(i) {
    return (
        <Square
            value={this.props.squares[i]}
            onClick={() => this.props.onClick(i)}
        />
    );
}
1
  • Try cols.push(this.renderSquare(j, i*col_count + j)); and renderSquare(key, i) { return ( <Square key={key} value={this.props.squares[i]} onClick={() => this.props.onClick(i)} /> ); } Commented Jul 25, 2017 at 16:44

3 Answers 3

1

I think your <Square /> components need keys.

renderSquare(i) {
return (
    <Square
        key={i}
        value={this.props.squares[i]}
        onClick={() => this.props.onClick(i)}
    />
  );
}
Sign up to request clarification or add additional context in comments.

3 Comments

Indeed, this solved it! But why was it not giving a problem in the 1-level loop implementation, but in the 2-level implementation? I am not quite getting that...
The first implementation didn't have an iterator spitting out Squares. If you look at the error message, you will see it only pops up when there is an iterator. Each child in an array or iterator should have a unique "key" prop.
oh wow... so it is not the same to do <Square /><Square /><Square /> than doing {arrayWithSquares}? In the second case, I need keys? That is something I did not know... Thanks!
1

I believe you're seeing the warning because in your 2nd implementation you also have the cols array which is basically a list of Square's. So your code is something like :

render() {
    let col_count = 3;
    let row_count = 3;

    let rows = [];
    for (var i = 0; i < row_count; i++) {
        let cols = [];
        for (var j = 0; j < col_count; j++) {
            cols.push(this.renderSquare(i*col_count + j));
        }

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

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

    );
}

4 Comments

Well, if keys were the items giving me problems, it would also cause a problem in the implementation with just 1 loop level, no?
In your first implementation you only have one array - rows.
so? What are the requirement so that "keys" are needed?
In rough terms, react needs a unique identifier for each array item in order to properly render and update them when state changes. As others have noted, you could send key as prop for the Square component.You can read more about Lists and Keys here : facebook.github.io/react/docs/lists-and-keys.html
0

You can use sum of iteration indexes like this:

  renderBoard(colsNum, rowsNum) {
    let boardSquaresArr = []
    for (let r = 0; r < rowsNum; r++) {
      let boardRowsArr = []
      for (let c = 0; c < colsNum; c++) {
        boardRowsArr.push(<span key={(r * colsNum) + c}>{this.renderSquare((r * colsNum) + c)}</span>);
      }
      boardSquaresArr.push(<div className="board-row" key={r}>{boardRowsArr}</div>);
    }
    return boardSquaresArr
  }

  render() {
    return (
      <div>
        {this.renderBoard(3, 3)}
      </div>
    );
  }

and benefit will be function renderBoard(colsNum, rowsNum)(you choose size of your board)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.