Resizable Div in React: mini project

Resizable Div in React: mini project

ยท

4 min read

Introduction ๐Ÿ™‹

I was trying to make a clone of codepen in React for that I had to figure out a way to create a resizable window to make a 2-pan resizable layout. Here's a sneak peek of what my solution gave me.

Code Sandbox preview

What we are building

here we will be building a div element and a handle to the right side of it which the user can drag to change the size of the div.

What things we will use

  • event.Window.offsetX method This method gives us the x coordinates of our cursor

  • useRef Hook: to get a reference of the handle.

  • useCallback Hook: to create a memorize function to remove a bug in our implementation.

  • CSS variable: Track and manipulate the width of the window.

Overview of how we are approaching this

  1. create 2 divs

    • The first div that needs to be resized, it will take its width from a CSS variable --width which will be declared inline

        <div styles={{ --width: "20px" }}> </div>
      
  2. Next, a second div which is a handle, will track on mouseDown and onMouseup events on it to start and stop tracking the cursor.

    • onMuseDown we will track x coordinates of cursor, then calculate the diff from start till the end then update --width variables value using useState which will intern change the width of the element.

Let's Start Coding ๐Ÿ’ป

Step 1. Create an empty Project

use can init a react project as you like.

After the project has been created, we'll do the basic ui setup

css

// App component
import "./app.css"
function App() {
  return(<div className="container">
    <div className="box" ></div>
    <div className="handle"></div>
  </div>)
}
export default App
/*Basic Styling src/app.css */
body {
  margin: 0px;
}
.container{
  display: flex;
  align-items: center;
  width: 100vw;
  height: 100vh;
  background-color: #CAD5E2;
  padding: 20px;
}
.box {
  background-color: #8D3DAF ;
  height: 200px;
  width: 200px;
}

.handle {
  background-color: #0D0D0D;
  height: 200px;
  width: 10px;
}

Step 2. Create an object with a style const [width, setWidth] = useState({ "--width": "200px" });

  • above we have created an variable width which is an object that holds css styles,

  • which is then passed to inline css.

  • and in our app.css file, we can use the variable from that css to set width of box

Step 3 Create Three functions for mouseUp mouseDown and mouseMove events

  1. handleWindowMouseMove : this will hold logic for updating the width state, for now, lets just console log the location of the mouse.

     const handleWindowMouseMove = (event) => {
         console.log(event.clientX);
     }
    
  2. hadnleMouseDown : this will be triggered when you click and hold you cursor on handle , we will use onMouseDown event to trigger it.

    This will add an event listener to the window object which will listen to our mouse movement

       function hadnleMouseDown(){
         window.addEventListener("mousemove", handleWindowMouseMove);
       };
    
  3. hadnleMouseUp : similar to handleMouseDown function it will be triggered onMouseUp event from handle, box and container.

    This will add an event listener to window

       function hadnleMouseUp()
         window.removeEventListener("mousemove", handleWindowMouseMove);
       }
    
  4. Now add this event Listeners to concerning elements

Now the handle Mouse Move event will log the location of mouse .

Take the coordinates and put them into styles

use setWidth to change css variables values.

This works but there is a bug .( We when we leave the mouse button, the event listner is not remove , therefore it will keep changing the sige even after we are not holding the button ).

Cause of this issue : on every re-render the function on muse down is triggerd add added to the event. and at the end only the latest listner is removed from the event and all previous are still there

Solution: we can use useCallback to memorise the handleWindowMouseMove function. this will persist the handleWindowMouseMove function across renders.

Implementing useCallback Hook Solution

just wrap the function definition of hadnleWidow... function.

This solves The issue we previously had but there is still an issue

issue: the width of box is not changing as desired it just randomly jums to a huge with it does not follow the handle properly

Reason: the cordinate we get from event.clientX is from the far left of the screen , on the other hand, our box starts from another position. this is clear in the digram below

Yup, thats it its this small of code for resizable div ๐Ÿ˜„

Here is how the final app.jsx looks like

import React, { useCallback, useState } from "react";
import "./app.css";

function App() {
  // obj for inline CSS
  const [width, setWidth] = useState({ "--width": "200px" });

  const handleWindowMouseMove = useCallback((event) => {
    // console.log(event.clientX)
    setWidth({ "--width": `${event.clientX}px` });
  }, []);

  function hadnleMouseDown() {
    window.addEventListener("mousemove", handleWindowMouseMove);
  }

  function hadnleMouseUp() {
    window.removeEventListener("mousemove", handleWindowMouseMove);
  }
  return (
    <div className="container" onMouseUp={hadnleMouseUp}>
      <div className="box" style={width} onMouseUp={hadnleMouseUp}></div>
      <div
        className="handle"
        onMouseDown={hadnleMouseDown}
        onMouseUp={hadnleMouseUp}
      ></div>
    </div>
  );
}

export default App;

You can use the same concept to make different types of the resizable component by adding handlers to different sides.

Follow me on twitter and Linkdin

ย