React.js -- Functional Components -- DOM manipulation, change the style of elements

772 views Asked by At

I am trying to manipulate the DOM using a functional component in React.js. I want the floating squares to rotate a certain amount of degrees when I hover over them, and I try to manipulate the elements in the DOM in my handleCursor function. The style says it is being updated on the console.logs to the right, so I'm not sure why the actual squares themselves don't do the rotation.

What I want is shown here in this codepen link: https://codepen.io/rauldronca/pen/MZjEBd

floating squares

Here's some JS code

import React, {useEffect, useRef} from 'react';
import '../css/home.css';

const Home = () => {
    // const bubbleRef = useRef(null);

    // useEffect(() => {
    //     if(bubbleRef.current) {
    //         bubbleRef.current.style.transform = 'translateX(200px)';
    //     }
    //     console.log(bubbleRef.current);
    // }, [bubbleRef])

    const handleCursor = (e) => {   
        const bubbles = document.querySelectorAll('.bub');
            
        const mouseX = e.pageX;
        const mouseY = e.pageY;
        
        bubbles.forEach(bubble => {
            const bubbleX = bubble.offsetLeft + 20;
            const bubbleY = bubble.offsetTop + 20;
    
            const diffX = mouseX - bubbleX;
            const diffY = mouseY - bubbleY;
            
            const radians = Math.atan2(diffY, diffX);
                    
            const angle = radians * 180 / Math.PI;
            
            bubble.style.transform = `rotate(${angle}deg)`;
            console.log(bubble);
        })
    }

    return (
        <div className='bubbleContainer'>
            <div className='bub1 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub2 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub3 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub4 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub5 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub6 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub7 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub8 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub9 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub10 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub11 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub12 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub13 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub14 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub15 bub' onMouseMove={e => handleCursor(e)}></div>
            <div className='bub16 bub' onMouseMove={e => handleCursor}></div>
        </div>
    )
}

export default Home;

Also, I tried using the useRef hook as well, but it was doing the same thing as my handleCursor function. The squares still would not rotate (I commented out my useRefs code)

1

There are 1 answers

0
Jared Smith On BEST ANSWER

Don't mix traditional imperative DOM manipulation and React unless you are really familiar with exactly how React works. Use declarative manipulations. In React, you'll have some state (like rotation) and your event handlers will update your state, and then React will detect this and automatically re-render the affected components that rely on that state. You will need a ref for each bubble here to get the offsets. In React you would normally just abstract them into their own component:

const Bubble = () => {
  const bubbleRef = useRef();
  const [rotation, setRotation] = useState(0);
  const calcAngle = (x, y) => {
    // Math goes here. You can use bubbleRef.current.whatever
    // to get the properties of the underlying DOM element.
    // You could make this a pure function and pull it out of
    // the component at the cost of adding more parameters,
    // I would but your call.
  };
  const handleMove = (evt) => {
    // Assuming you pull the relevant maths into
    // a separate function
    setRotation(calcAngle(evt.pageX, evt.pageY));
  };

  // NOTE: use className, not class in React
  return (
    <div
      className="bub"
      onMouseMove={handleMove}
      ref={bubbleRef}
      style={{
        transform: `rotate(${rotation}deg)`,
      }}>
    </div>
  );
};

and now your original component looks like this:

const Bubbles = () => (
  <div className="bubbleContainer">
    <Bubble />
    <Bubble />
    <Bubble />
  </div>
);