Search code examples
reactjsmedia-queriesresponsive

React execute script if window width


I have a button in React that executes a function onClick. I want to get rid of the button, and instead programmatically execute the function if window width < 1000px.

A restriction is that I can not add a plugin.

Here's what the code looks like...

// Do I need useState, useEffect?
import React, { PureComponent } from "react";

class MainNav extends PureComponent {
state = {
  // Does something go here? What goes here and how do I use
  // state to execute the function?
  navIsCollapsed: false,
};

// this controls rendering of huge images
toggleShowImages() {
  this.setState({
    navIsCollapsed: !this.state.navIsCollapsed,
  });
}

// I want this to be executed by width < 1000
handleSideNavToggle = () => {
  this.toggleShowImages(); // controls if React renders components
  document.body.classList.toggle("side-nav-closed");
}

Here's render the button that's currently executing the function. I want width < 1000 to programmatically execute its function.

// window width < 1000 should execute this function
<div onClick={this.handleSideNavToggle}>Don't render huge images</div>

// here are the images the function conditionally renders
<should show images && 
  <div>Massive huge image</div>
  <div>Massive huge image</div>
  <div>Massive huge image</div>
>

I could use CSS media query to show or hide the massive images I don't want, but that's horrible use of React.

I've looked and tried to implement similar questions on SO that either invoke plugins, are out of date, or the use case is too different (for example, "re-render everything based on screen size"). I've also tried to React-ify vanilla javascript. This seems like it ought to be simple to do but I can't make it work.

Any React wizards out there who can answer with a clean, efficient solution?


Solution

  • Use the above method that Mathis Delaunay mentioned to get viewport/window width, then to get rid of that button. Just simply add a condition to whether render it or not and then watch on state changes to trigger the function. Here I use hooks to do it

    import React, { useEffect, useState } from "react";
    import ReactDOM from "react-dom";
    
    function App() {
      const [width, setWidth] = useState(window.innerWidth);
    
      useEffect(() => {
        function handleResize() {
          setWidth(window.innerWidth);
        }
        window.addEventListener("resize", handleResize);
        return () => window.removeEventListener("resize", handleResize);
      }, [width]);
    
      useEffect(() => {
        width < 600 && handleSideNavToggle();
      },[width]);
    
      function handleSideNavToggle() {
        console.log("toggle it");
      }
    
      return (
        <div className="App">
          {width > 600 && (
            <button onClick={() => handleSideNavToggle()}>
              Don't render huge images
            </button>
          )}
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    Here is a working example. I set the width to be handled as 600 to make it easy to see.

    https://codesandbox.io/s/react-hooks-counter-demo-w9wgv