Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found

Submitted

Tic Tac Toe - React and Styled Components

#react#styled-components
P
Daveβ€’ 5,245

@dwhenson

Desktop design screenshot for the Tic Tac Toe game coding challenge

This is a solution for...

  • HTML
  • CSS
  • JS
3intermediate
View challenge

Design comparison


SolutionDesign

Solution retrospective


This is my second project using React and first using styled components. I put some details in the readme, but some issues/questions I have are:

  • One issue I had with styled components is keeping track of whether the components in the file were now React components or styled components! I also struggled mapping the styled components to the rendered DOM in the dev tools, which made debugging a bit tricky in some cases. How to people manage this?
  • 'React.useEffect` is asking for additional dependencies in a couple of places in board.js (see FIXMEs). The only way I could get the app deployed was to ignore them. If I add them as suggested I end up with infinite loops. I wasn't sure how to deal with this if any one knows how to deal with this I would love to know.
  • Board.js is a bit of a monster, I had a look at refactoring things, but I couldn't see any obvious ways to do this. I considered moving the remaining game logic functions out, but they all change state so this didn't seem sensible. Any suggestions would be welcome!

Thanks in advance for any suggestions and feedback. Cheers Dave

Community feedback

P

@christopher-adolphe

Posted

Hi @Dave πŸ‘‹,

You did a great job on this challenge. πŸ‘ I had a great time playing the game too. πŸ˜‰

I saw you question about the issues you are having with dependency array of React's useEffect hook. I had a quick look at your code for the Board.js component and here are a few things that I have noticed which might be why you are facing those issues. I'll give you some rules/guidelines that I use myself when I work with the useEffect hook and I'll try to keep it simple so that you'll know where to go from here.

Rule 1: Don't ignore the warnings about the dependency array

  • We get warnings when React sees we are doing something wrong. You should take a step back and carefully review your implementation of the useEffect hook.

Rule 2: Know the data types of the dependencies being passed to the array

  • This is where most trouble come from. We should carefully check the data types of the different elements being passed to the dependency array. If the dependencies are primitive data types (i.e: string, number, boolean, null, undefined) we don't have much to worry as they are compared by value. But if they are reference data types (i.e array, object, function, date), the useEffect hook will compare them by reference (i.e their pointer in memory) and each time the component renders, we create a new reference to those dependencies and as the useEffect hook detects they are different, it reruns hence resulting in an infinite loop. 😱 Hopefully, React provides us with tools to work around this problem; they are the useMemo and useCallback hooks. Consider the useEffect below which was extracted from your Board.js component:
React.useEffect(() => {
    if (gameType !== "CPU") return;
    if (marker !== turn) {
      renderSquareChoice(computerMove(squares, marker));
    }
  }, [gameType, turn]);

renderSquareChoice is a function inside your component and you are using it inside useEffect, so React prompts you that it needs to be added to the dependency array. Now, you add it as a dependency, you get an infinite loop because renderSquareChoice is a function meaning it is a reference data type. To fix this, you need to wrap the renderSquareChoice with useCallback like so:

const renderSquareChoice = useCallback((square) => {
    if (status || squares[square]) return;
    const nextSquares = [...squares];
    nextSquares[square] = turn;
    setSquares(nextSquares);
  }, []);

And you will be able to add it to useEffect as a dependency without causing an infinite loop. NOTE: In the above code, useCallBack also has a dependency array, you might need to add status and squares as dependencies.

  • There are cases where we don't need to pass the entire object or array as a dependency to useEffect. For example, if my component has a person state/prop but my effect is only dependent on the age property of that person state. All we need to do is to pass this age property to useEffect like below:
const [ person, setPerson ] = useState({
  name: 'Chris',
  age: 36,
  role: 'guest'
});

useEffect(() => {
  // some side effect that needs to run whenever the age of `person` state is mutated
}, [person.age]);

So we are not passing the entire person state which an object and therefore a reference data type because each render of the component would create a new reference for the person state. Instead, we pass only the age property which is a number and therefore a primitive data type. I illustrated a simple case here but for complex cases the useMemo hook would be more appropriate. This feedback is already very long, I suggest you to read more about useMemo.

Rule 3: Use the functional variant of the state setter function inside useEffect

  • Whenever useEffect is dependent on a state which is mutated inside it, you should use the functional variant of the state setter function. Taking another example extracted from the Board.js component:
// Update total scores
  React.useEffect(() => {
    if (status === null) return;
    const newScore = { ...score };
    newScore[status] += 1;
    setScore(newScore);
  }, [status]);

This useEffect should be refactored as follows:

// Update total scores
React.useEffect(() => {
  if (status === null) return;
  
  setScore((prevScore) => {
    const newScore = { ...prevScore };

    newScore[status] += 1;

    return newScore;
  });
}, [status]);

By doing so, our useEffect is no more dependent on score state and hence it isn't needed in the dependency array.

I know that's a lot to read as feedback but I tried to keep it as lean as possible. useEffect is a big topic in itself. I also got these issues while tackling the Coffeeroasters challenge with React (still in progress πŸ˜‰). The more you practice, the better you'll get at it.

I hope this helps you as a starting point.

Here are some resources from Jack Herrington that helped me:

Keep it up.

Marked as helpful

1

P
Daveβ€’ 5,245

@dwhenson

Posted

@christopher-adolphe wow thanks so much for taking the time to write such a comprehensive response.

I had learnt a little bit about useMemo and useCallback, but couldn't quite see how to apply them to my code. You have made this super clear and I'll be going through everything you wrote again in much more detail and doing a refactor.

I'll check out those resources too. Thanks once again for the feedback it is really, really appreciated!!

0
P

@christopher-adolphe

Posted

@dwhenson you are welcome.

I'm happy to help and glad to see this was helpful to you. πŸ™‚

See you on the next one.

Best regards.

1
Miran Leginβ€’ 620

@miranlegin

Posted

Hi Dave, congratulations on completing this challenge!

I'm no expert in React by any means, even struggle in Javascript a bit so can't comment on that part. So I will comment on some things that I can see and think they can slightly improve user experience.

I knew when i saw the notification that you completed another challenge that i won't be disappointed and you prove me right again. I think you did an awesome job on this one. Everything is working like expected, there were no hiccups or anything strange to happen. I tried it on a 27" Desktop and a 6" Smartphone. Restarting browser to continue where you left of is working like expected.

So let's get started with some of the minor improvements in my opinion:

  1. when you are playing with the mouse i would love the see the cursor:pointer on the buttons. It is such a minor thing but in my opinion it is definitely needed here
  2. on the intro screen where you are choosing your opponent i would love to see marginally taller buttons (CPU vs PLAYER), it is slightly awkward to hit it on the smartphone device
  3. when your opponent is CPU just some delay to give impression that CPU is calculating his next move, to be more immersed in the experience of a game

Looking forward to see you next challenges!

Cheers, Miran

Marked as helpful

1

P
Daveβ€’ 5,245

@dwhenson

Posted

@miranlegin thanks so much for the feedback - and kind words! - it is much appreciated.

I think I've addressed the issues you suggested I deal with above. The first two were pretty simple, the last was a bit tricky, but I think I managed. I made it a bit random too so it seems less mechanical.

Do let me know if you spot anything else. I'm new to React so it's helpful to poke around things and try and work out what's going on!

Thanks again Dave

0

Please log in to post a comment

Log in with GitHub
Discord logo

Join our Discord community

Join thousands of Frontend Mentor community members taking the challenges, sharing resources, helping each other, and chatting about all things front-end!

Join our Discord