Tackling the Circle Intersection Problem in a React Interview

Tackling the Circle Intersection Problem in a React Interview

The Interview Question

During a recent technical interview, I was given an interesting challenge:

"You need to build a React application where users can click anywhere on the screen to generate circles of random sizes. The circles should change color if they intersect with existing ones. Additionally, after every third click, the canvas should reset."

I had to quickly break down the problem and come up with an efficient way to handle circle positioning, intersection detection, and state management in React.

My Thought Process & Approach

When tackling this problem, I broke it down into the following key components:

  1. Tracking Click Events - Each click should generate a new circle at the clicked position.

  2. Generating Random Radius - The circle's radius should be randomly assigned between 20 and 200 pixels.

  3. Detecting Circle Intersections - If the new circle overlaps with any existing ones, it should turn red; otherwise, it remains blue.

  4. Managing State with React Hooks - Using useState to store and update the list of circles.

  5. Resetting After Three Clicks - The canvas should clear every third click to keep the UI fresh and test state management.

With these requirements in mind, I began implementing the solution.

The Solution

Here’s the final implementation of my solution:

import { useState, useEffect } from "react";
import "./App.css";

function App() {
  const [circles, setCircles] = useState([]);
  const [clickCount, setClickCount] = useState(0);

  useEffect(() => {
    console.log(circles);
  }, [circles]);

  // Function to check if a new circle overlaps any existing ones
  const checkIntersection = (newCircle) => {
    return circles.some((circle) => {
      const dx = circle.x - newCircle.x;
      const dy = circle.y - newCircle.y;
      const distance = Math.sqrt(dx * dx + dy * dy);
      return distance <= circle.radius + newCircle.radius;
    });
  };

  const handleClick = (e) => {
    if (clickCount === 2) {
      setClickCount(0);
      setCircles([]);
      return;
    }

    const radius = Math.random() * (200 - 20) + 20;
    const newCircle = {
      x: e.clientX,
      y: e.clientY,
      radius: radius,
      intersects: checkIntersection({ x: e.clientX, y: e.clientY, radius }),
    };

    setClickCount((count) => count + 1);
    setCircles((prev) => [...prev, newCircle]);
  };

  return (
    <div
      onClick={handleClick}
      style={{ width: "100vw", height: "100vh", backgroundColor: "#f0f0f0", position: "relative" }}
    >
      {circles.map((circle, idx) => (
        <div
          className="circle"
          key={idx}
          style={{
            position: "absolute",
            width: `${circle.radius}px`,
            height: `${circle.radius}px`,
            top: `${circle.y - circle.radius / 2}px`,
            left: `${circle.x - circle.radius / 2}px`,
            backgroundColor: circle.intersects ? "red" : "blue",
            borderRadius: "50%",
            pointerEvents: "none",
          }}
        ></div>
      ))}
    </div>
  );
}

export default App;

Code Explanation

1. Tracking Click Events

  • The handleClick function is triggered when the user clicks anywhere on the screen.

  • It captures the x and y coordinates from the click event (e.clientX and e.clientY).

2. Generating Random Radius

  • The radius of the new circle is randomly generated using:

  •   const radius = Math.random() * (200 - 20) + 20;
    
  • This ensures a range between 20px and 200px.

3. Detecting Circle Intersections

  • The checkIntersection function calculates the distance between the new circle and all existing ones:

  •   const dx = circle.x - newCircle.x;
      const dy = circle.y - newCircle.y;
      const distance = Math.sqrt(dx * dx + dy * dy);
      return distance <= circle.radius + newCircle.radius;
    
    • If the distance is less than or equal to the sum of the radii, the circles intersect.

4. Managing State with useState

  • circles state stores the list of circles created.

  • clickCount tracks how many clicks have occurred before resetting.

  • The setCircles([...prev, newCircle]) updates the list of circles after every click.

5. Resetting After Three Clicks

  • Every third click (clickCount === 2) clears the screen:
    if (clickCount === 2) {
      setClickCount(0);
      setCircles([]);
      return;
    }
  • This ensures a fresh canvas every three clicks.

Challenges Faced

  1. Handling Click Event Properly

    • Initially, I had an issue where clearing the circles happened after adding a new one. Fixing the sequence of state updates resolved this.
  2. Intersection Accuracy

    • At first, I used <= but had rounding errors with floating points. I ensured calculations were precise with Math.sqrt(dx * dx + dy * dy).
  3. Preventing Circles from Blocking Clicks

    • I used pointerEvents: "none" on circles so that clicks would still register properly.

Key Takeaways

Breaking down the problem helped me focus on one feature at a time.
Using React's useState efficiently made it easy to track clicks and circles.
Mathematical precision is important when handling geometric calculations.
Handling state updates properly ensures smooth and expected UI behavior.

Final Thoughts

This question tested my ability to work with React state, event handling, mathematical calculations, and UI updates. I enjoyed the challenge, and solving it boosted my confidence in handling complex UI interactions.

Would you approach this problem differently? Let me know your thoughts! Also, I will be adding more question.