React component rendering twice due to width awareness

In a React component that I've developed, I have utilized ResizeObserver to track its own width. Within this component, two divs are rendered with each having a "flex: 1" property to ensure equal width distribution. Under certain conditions, such as when the component is too small and specific state requirements are met, the first div's display is set to "none", allowing the second div to occupy the entire width of the flex container.

Although the component effectively responds to changes in browser width, an issue arises when there's a sidebar that can be toggled open or closed in my application. When the sidebar is closed and sufficient width becomes available due to this action, the component should recognize it and readjust to display the "dashboard_monitors" div once again. However, a glitch occurs where initially the sidebar is closed, and the second "dashboard_activity" div occupies the full width before eventually updating to show the "dashboard_monitors" div, resulting in a visible shift as the component realigns each div with equal width.

To harmonize the disappearance of the sidebar and the component update simultaneously, preventing the perceptible jump, a strategy needs to be implemented but I'm uncertain about how to achieve this.

The following is the code snippet for the mentioned component:

import './Dashboard.css'
import MonitorComponent from '../../components/Monitor/Monitor';
import DetailComponent from '../../components/Monitor/Detail';
import { Monitor } from '../../monitor'
import { useEffect, useMemo, useState } from 'react';
import { useWidth } from '../../hooks/useWidth';

interface DashboardProps {
  monitors: Monitor[]
  focused: Monitor | null,
  drafting: boolean,
}

export default function Dashboard(props: DashboardProps) {
  const {
    monitors,
    focused,
    drafting,
  } = props;
  const isEditing = useMemo(() => focused || drafting, [focused, drafting]);
  const { width: dashboardWidth, ref: dashboardRef } = useWidth<HTMLDivElement>();

  const [sorted, setSorted] = useState<Monitor[]>([])

  useEffect(() => {
    const sorted = monitors.sort((a, b) => {
      if (a.strategy.name > b.strategy.name) {
        return 1;
      } else {
        return -1;
      }
    });
    setSorted(sorted);
  }, [monitors])

  const dashboardClasses = ['dashboard', isEditing && dashboardWidth < 700 ? 'single' : '']

  return (
    <div className={dashboardClasses.join(' ')} ref={dashboardRef}>
      <div className="dashboard_monitors">
        {sorted.map((n, i) =>
          <MonitorComponent key={i} monitor={n} />)
        }
      </div>

      {isEditing ?
        <div className="dashboard_activity">
          <DetailComponent monitor={focused} />
        </div>
        : null}
    </div>
  )
}

Here is the CSS styling applied to the component in "Dashboard.css":

.dashboard {
    display: flex;
    height: 100%;
}

.dashboard.single .dashboard_monitors {
  display: none;
}

.dashboard.single .dashboard_activity {
  margin-left: 0;
}

.dashboard > * {
  flex: 1;
  overflow-x: hidden;
  overflow-y: auto;
}

.dashboard_activity {
  margin-left: var(--padding-2);
}

.dashboard_monitors {
    display: grid;
    gap: var(--padding-2);
    grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
    grid-auto-rows: 200px;
    padding-right: var(--padding-0);
    padding-bottom: var(--padding-0);
}

The ResizeObserver functionality is encapsulated in the useWidth hook presented below:

import { useState, useRef, useEffect } from "react";

export const useWidth = <T extends HTMLElement>() => {
    const [width, setWidth] = useState(0);
    const ref = useRef<T>(null);

    useEffect(() => {
        const observer = new ResizeObserver((entries) => {
            setWidth(entries[0].contentRect.width);
        });
        const refElement = ref.current;

        if (refElement) {
            observer.observe(refElement);
        }
        return () => {
            refElement && observer.unobserve(refElement);
        };
    }, []);

    return { width, ref };
};

If you have any insights or inquiries regarding this matter, I appreciate your valuable input. Thank you for your time.

Answer №1

In my previous attempts to solve this issue, I experimented with container queries. However, I found that I needed the query to be able to recognize my React state as well. It wasn't until recently that I stumbled upon a new method. This approach allows the container query to interact with the state and gives you full control from CSS, eliminating the double paint problem I encountered when handling it solely from React code.

To address this, I implemented a simple solution where the dashboard assigns itself a class based on its editing status:

const dashboardClasses = ['dashboard', isEditing ? 'editing' : '']
<div className={dashboardClasses.join(' ')}>

I then configured the container queries to monitor for a reduced width and apply additional styles if we are in editing mode:

@container dashboard-container (max-width: 800px) {
  .dashboard.editing .dashboard_monitors {
    display: none;
  }

  .dashboard.editing .dashboard_activity {
    margin-left: 0;
  }
}

This issue has been successfully resolved, but unfortunately, I have to wait two more days before accepting my own answer.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Struggling to incorporate blocks into Jade for Express. Encountering errors like "Max Stack Size Exceeded" and issues with setHeader

I am currently using Express along with a simple express-generator server that I created. My first task was to focus on creating the view layout and extending the index page, but unfortunately, I have encountered some challenges. Throughout my process, I& ...

Minimize data once more using various key criteria

In my current setup, I have created a view that counts reports for different cities. function (doc) { if(doc.type == "report") { emit(doc.city_name, null); } } Using the _count reduce function, I get the following values: {'key': &a ...

Guide to changing the background colors of multiple elements when hovered over by the mouse?

I want to customize my website's search bar by changing the background color when it is hovered over. Currently, the search bar has two main elements: the text box and the submit button. I have successfully programmed the text box element to change to ...

The navigation bar is missing from the screen

Where is the navbar in this snippet? Did I overlook something important? If so, what? <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" /> <div class="container"> <div class="navbar nav ...

Break the line using HTML select option and set it as the value

I am working on a select box where I want to include a value for a line break. <select> <option value=";">Semicolon</option> <option value=",">Comma</option> <option value=".">Dot</option> <opti ...

Is there a way to remove the ring shadow in TailwindCSS?

Here is a visual representation of the issue I'm facing (refer to the image): Check out the Image After inspecting with Chrome, it seems like the problem is connected to --tw-ring-shadow. I attempted to use classes like ring-0 and ring-offset-0 (men ...

Error: Undefined object while trying to access 'injection' property

After updating to React 16.x, I encountered the following error: Uncaught TypeError: Cannot read property 'injection' of undefined at injectTapEventPlugin (injectTapEventPlugin.js:23) at eval (index.js:53) at Object.<anonymous> ...

Find out the initial and final dates of a React calendar

Currently, in my React project, I am utilizing the "react-big-calendar": "^0.20.3", At the moment, I am employing the getDrilldownView={(targetDate, currentViewName, configuredViewNames) => this.updateDate(targetDate, currentViewName, configuredViewNam ...

How to stop AngularJS onClick event from causing scroll to jump to top of

As I work on developing a website using Angular JS and Bootstrap, I encountered an issue with the hamburger menu button on my homepage. Whenever I scroll halfway down the page and click the hamburger icon, it automatically scrolls me back to the top of the ...

Guide on embedding PHP code into a HTML div element using JQuery

I am having trouble loading PHP code into a div tag to display the results. The code I have listed below does not seem to work for me. If anyone can offer assistance in resolving this issue, I would greatly appreciate it. Here is the code snippet: <h ...

What is the process to activate a resize event with nvd3?

I am trying to figure out how to make the resize event trigger on nvd3 for AngularJS. The issue I am facing is that when I add line breaks in the labels of the pie chart and then resize the window, the labels revert back to a single line. What I want is ...

The color input click event does not work properly when triggered within a context menu event

This may sound a bit complicated, but let me try to explain it clearly. I'm working on a web app where I want users to be able to change the background color of certain divs. I plan to use a color picker interface for this purpose and utilize the con ...

What could be causing the error with React npm start and webpack-dev-server version 3.11.1?

I encountered an error while running npm start in my React application: The react-scripts package provided by Create React App requires a specific dependency: "webpack-dev-server": "3.11.1" Do not attempt manual installation as your ...

Jasmine tests for AngularJS directive failed to invoke the link function

I can't figure out why the link function of my directive isn't being called in a Jasmine test. I've created a simple example to illustrate. Here is the code for my directive (TestDirective.js): 'use strict'; angular.module(&ap ...

Extract data from axios and display it in a Vue template

Having trouble displaying a value inside a div tag in my nuxt app. An error message keeps popping up saying "Cannot read property 'free_funds' of undefined. Still getting the hang of Axios and Nuxt. Could it be that Bootstrap requires JQuery to ...

I am consistently encountering the npm ERR! code 1 error when trying to run the react-scripts build command in my code

I have successfully developed a create-react-app application on my Windows 10 workstation using node v12.19.0 and npm v6.14.8. I have been able to run npm start and npm run build without any issues and access the app smoothly. However, when attempting to ...

The Symfony API failed to generate a response

There seems to be a problem when trying to link the Symfony API with a React application. The API is not providing any response, even though it works fine when accessed directly through the link. ApiDirectURL Fetching this data from the React app is yiel ...

Increase the padding for each child element within its corresponding parent

Is it possible to add padding to child items within each "folder" element without using JavaScript if there is more than one? This scenario would be hardcoded as follows: folder folder inner { padding-left: 14px; } folder folder folder inner { pad ...

Are you encountering difficulties while managing back pressure as anticipated when applying node request or axios in combination with streams for downloading and extracting files?

We have encountered a peculiar problem while trying to download and decompress a large file of approximately 6 GB (which decompresses to around 64 GB) using the HTTP protocol. To achieve this, we are utilizing either Node.js' request library or axios. ...

searchByTextContentUnderListItemAnchorTag

I would like to utilize the getByRole function for writing my test. However, I am encountering issues when using linkitem or 'link' as the role. It seems that I cannot find the desired element. // encountered error TestingLibraryElementError: The ...