Tutorial from official React page

Tutorial from official React page
0

#1

I was doing this tutorial from official React page. In it, as a additional credits you must add additional features. I did it all of them to the letter. Fifth feature “When someone wins, highlight the three squares that caused the win.” however it doesn’t work properly. When you win, you need one additional click on tic tac toe table to highlight winning combo. Although you can click on wherever point on table to highlight … My question is: Why is required this additional click, why it doesn’t highlight on last click just as it proclaims winner?
My codepen.


#2

At a guess, are you checking for a winner BEFORE you add the clicked square to the array of played moves? If so, then it will only show a win on the turn AFTER the win.

From what I’m seeing, that seems a possible thing. Check for win at the END of the click handler, rather than at the beginning.


#3

Checking for winner is working flawlessly. Problems is applying different style on winning squares.
Step 1: Inside a main component called “Game” a child component called “Board” is called.
Step 2: Board component has a prop called “highlight” with in turn holds value of an array whose members are(three of them) are values of fields of winning combination e.g. 0,1,2 or 0,3,6 etc …
Step 3: Component board renders child called “Square” and it send previously said prop as a value inside its own prop called “winningCombo”.
Step 4: Component Square then evaluates if its received this prop and if its received it, then it compares prop’s values, previously mentioned 0,3,6 etc, with it’s elements other props an id’s and if it’s same value as it’s id, then it paint it’s background in flushed green and sets it’s font color to red.
By my account, passing these props completely from top to bottom , somewhere, somehow … Maybe if i encompass whole render function in main component in if statement which will get executed if “highlight” prop got it values … PS: “highlight” state prop is undefined, hence it’s “falsy” by default if that helps you …


#4

Can you point out the section of your Codepen which deals with marking the winning squares? There are so many comment sections (in a different language) that it makes it difficult for me to review the code and I want to get right to helping you.

I worked through this tutorial a week ago and developed a solution for marking the squares. Currently, I am refactoring the tutorials code to get rid of the extra call to calculateWinner. In the tutorial code there is one in handleClick and another in the Game component’s render method. I am close to removing the duplicate, but I got side-tracked with other stuff, so I have not completed my refactor.


#5

function Square(props) . It creates individual squares and is where is determined which style is applied to individual square element depending on it’s id prop. It’s a top one, just scroll upwards to the top.


#6

The problem with your renderSquare method, is when the last square is clicked (which defines the winner), it only has the last value you passed via the winningCombo prop. That value when the square is rendered is null.

My Square function is different than yours (see below):

function Square(props) {
  const classes = `square ${props.win ? 'winner' : ''}`;
  return (
    <button className={classes} onClick={props.onClick}>
      {props.value}
    </button>
  );
}

I pass a prop named win which contains a value of true or false. This comes from my renderSquare method which accepts a second argument (the true or false value) which represents if this square has already been determined to be a winner. I keep track of all the winning squares via an argument passed to the squared method via the Board component’s render method. In the same render method, I use a modified version of calculateWinner to create create a winning squares array which gets passed as an argument to the squared method.

  squared (winningSquares = []) {
    const arr = [0, 1, 2];
    return arr.map(row => (
      <div className="board-row" key={row}>
        {
          arr.map(col => {
            const squareIdx = row * 3 + col;  
            return this.renderSquare(squareIdx, winningSquares.includes(squareIdx))
          })
        }
      </div>
    ))
  }  

I think the key to solving your issue is that you need to determine the winner and know which square indexes have the mark of the winner, and then pass this information to the squared method which ends up rendering the Square components.


#7

Aha … So somewhere here

cols.push(this.renderSquare(i * 3 + j));

i should pass prop something like this:

squared = (prop) => {
....

cols.push(this.renderSquare((i * 3 + j),props.someProp));

...
}

Is this sort of concept that you’re thinking of?


#8

Yes, something along those lines. Ideally, someProp would be an array of all the winning square indices.


#9

I already have that in the form of state named a “highlight” which is passed down as a prop. Now it’s only left to find a way to implement suggestions.


#10

Well, i didn’t quite used your suggestion, but i did it anyway. Care to take a gander? Here it is. Are these additional tasks that i done, done in proper React manner?


#11

I would definitely try to make the following more DRY (Do Not Repeat Yourself). In fact, you have several sections of code which code be simplified. This is less a React thing, but more of an overall improvement you could make it the code. Less Code === Better Readable and code management.

    if (winner) {
      return (
        <div className="game">
          <div className="game-board">
            {/*current.squares promenljiva je u stvari niz iz objekta iz state-a. prop-u squares iz board komponente, te u njoj square komponente, dodeljuje se prop i vrednost za prop. Salje se u Board koji ga prosledjuje Square(value). handleClick(i) takodje vodi do istih komponenti, a parametar je u stvari indeks kliknutog dugmeta.*/}

            <Board
              squares={current.squares}
              winningCombo={winner}
              onClick={i => this.handleClick(i)}
            />
          </div>
          <div className="game-info">
            <button onClick={this.reArrange}>ReArrange</button>
            <div>{column}</div>
            <div>{row}</div>

            <div>{status}</div>
            {/*Prikazivanje vrednosti prethodno definisane promenljive.*/}
            <ol id="myList">{movesCopy}</ol>
            {/*Prikazivanje vrednosti prethodno definisane promenljive.*/}
          </div>
        </div>
      );
    } else {
      return (
        <div className="game">
          <div className="game-board">
            {/*current.squares promenljiva je u stvari niz iz objekta iz state-a. prop-u squares iz board komponente, te u njoj square komponente, dodeljuje se prop i vrednost za prop. Salje se u Board koji ga prosledjuje Square(value). handleClick(i) takodje vodi do istih komponenti, a parametar je u stvari indeks kliknutog dugmeta.*/}

            <Board
              squares={current.squares}
              onClick={i => this.handleClick(i)}
            />
          </div>
          <div className="game-info">
            <button onClick={this.reArrange}>ReArrange</button>
            <div>{column}</div>
            <div>{row}</div>

            <div>{status}</div>
            {/*Prikazivanje vrednosti prethodno definisane promenljive.*/}
            <ol id="myList">{movesCopy}</ol>
            {/*Prikazivanje vrednosti prethodno definisane promenljive.*/}
          </div>
        </div>
      );
    }

How about just conditionally sending winner via the winningCombo prop?

    return (
      <div className="game">
        <div className="game-board">
          {/*current.squares promenljiva je u stvari niz iz objekta iz state-a. prop-u squares iz board komponente, te u njoj square komponente, dodeljuje se prop i vrednost za prop. Salje se u Board koji ga prosledjuje Square(value). handleClick(i) takodje vodi do istih komponenti, a parametar je u stvari indeks kliknutog dugmeta.*/}
          <Board
            squares={current.squares}
            winningCombo={winner ? winner : null}
            onClick={i => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <button onClick={this.reArrange}>ReArrange</button>
          <div>{column}</div>
          <div>{row}</div>

          <div>{status}</div>
          {/*Prikazivanje vrednosti prethodno definisane promenljive.*/}
          <ol id="myList">{movesCopy}</ol>
          {/*Prikazivanje vrednosti prethodno definisane promenljive.*/}
        </div>
      </div>
    );

#12

Thank you. Forgot i can do that

Still thinking in regular js.


#13

That is regular JS. It is just the ternary operator.

FYI - After looking at your code again, the following would work also:

          <Board
            squares={current.squares}
            winningCombo={winner}
            onClick={i => this.handleClick(i)}
          />

without the ternary operator, because winner is either true or null at any given point during the game.


#14

Ha … didn’t thought about null as “falsy” vs “truthy”, just as a one more value to evaluate … Good catch.