Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accelerate - Karolina Benitez #3

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

karolina-benitez
Copy link

No description provided.

Copy link

@anselrognlie anselrognlie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job!

Your tests are all passing, and your code is well-organized.

One thing to focus on is how to work with container types in React. Your logic for updating squares and checking for the winner definitely work, but you're side-stepping the squares state a little bit. The squares reference never gets updated, so it never triggers a re-render. Fortunately, other pieces of state do get updated, resulting in the re-render we need. Check the comments in App.js for details.

The overall structure and approach looks great though!

@@ -13,6 +13,7 @@

# misc
.DS_Store
.env

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious what prompted the inclusion of .env here. Were you tracking something in the .env file?

return <button
className="square"
onClick={()=>{onClickCallback(id)}}
id={id}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's not really a need to set the id attribute here. The anonymous function in your onClick handler is already holding onto the id value through closure semantics.

// Component to alert a parent
// component when it's clicked on.

const Square = ({ id, value, onClickCallback } ) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice inline destructuring usage!

Comment on lines +23 to +29
let destructuredSquare = []

destructuredSquare.push(...squares[0])
destructuredSquare.push(...squares[1])
destructuredSquare.push(...squares[2])

return destructuredSquare

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use a loop to generalize this sort of function, or if you know that there are always only 3 inputs, we could rewrite this as:

    return [...squares[0], ...squares[1], ...squares[2]];

// squares is a 2D Array, but
// you need to return a 1D array
// of square components
const squaresArray = destructureMatrix(squares)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job writing a method to help flatten the matrix! Also, check out the flat() method that exists on JS arrays.

if(squares[i][j].id === squareID && squares[i][j].value === ''){
squares[i][j].value = currentPlayer
updateCurrentPlayer(currentPlayer, setCurrentPlayer)
setSquares(squares)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out this call has no effect. You are processing the squares data directly, flipping the space corresponding to the clicked square to the current player. But since the squares reference is the same reference that React was already holding onto, this call to setSquares doesn't trigger a re-render. But we do see a re-render, so what's going on?

Since the current player gets flipped, that triggers the re-render. The squares collection held by React was updated, so when it renders the next view, we see the changes, but the setSquares had nothing to do with it.

Recall the general pattern for container objects (arrays, dictionaries, and general objects) in React involves making a copy of the existing values into a new value and updating the value held by the copy. This allows React to see the change, and also ensures that all the code that was rendered together has a consistent view of the data that was part of the render.

for(let i = 0; i < squares.length; i++){
for(let j = 0; j < squares[i].length; j++){
if(squares[i][j].id === squareID && squares[i][j].value === ''){
squares[i][j].value = currentPlayer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than updating the squares contents directly, it's generally preferable with React to make a copy and update the copy.

}
if(possibilities[i][0] === possibilities[i][1] && possibilities[i][1] === possibilities[i][2]){
if(possibilities[i][0] === 'x'){
x += 1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We know that we're calling this function after every move, so as soon as we find three in a row (so long as it's not three blanks) we can stop looking. There should never be a case where we find more than 1 three in a row of xs and os.

setWinner(null)
}
if(x === 0 && o === 0 && !empty)
setWinner('Tie')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would personally be more inclined to introduce an additional boolean piece of state specifically to to track whether the game was tied. If you really prefer using the same winner state, then I'd suggest making a const to hold the special string indicating the tie condition. The same constant could then be used later when you're building the game result string.

Making a single const value helps protect us from typos in strings, which is always a risk with magic values.

Comment on lines +108 to +110
{winner === 'Tie' ? <h2 className='tieGame'>The game is tied!</h2> : ''}
{winner === null ? <h2>The current player is {currentPlayer}</h2> : ''}
{winner === 'o' || winner === 'x'? <h2>Winner is {winner.toLowerCase()}</h2> : ''}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be inclined to pull this out of the markup here and into code that runs before the JSX return stuff, or into a helper function. There's sort of two things going on here: figuring out the className, and figuring out the h2 contents. Calculating those ahead of time before getting here can help make that logic more visible, and make the markup here a little clearer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants