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

When you hover over a dropdown menu, the content underneath it is raised slightly and a margin is formed on the right side of the page

I am facing some technical difficulties with my photography website, specifically on the "Contact" and "Bio" pages. The issue arises when the screen is resized, causing layout changes. When hovering over the "Albums" menu tab and the drop-down menu appears ...

Guide to connecting two separate components from distinct pages in React/MUI

My setup involves two pages: Appbar.js, where I have a top appbar and a Navigation Menu Icon Button that triggers the display of my Drawer (Material UI) in TempDrawer.js. Initially, everything worked fine when all the code was placed in the Appbar.js file ...

Having Trouble with Styling FormHelperTextProps in React Material UI TextField

Struggling to customize the helper text displayed with the TextField Component from Material UI? You can refer to the official documentation here for more information. To style this helper text, you can utilize FormHelperTextProps as detailed in the API ...

How can I activate a function or pass a selected value to a different scope variable using AngularUI Bootstrap Datepicker?

Check out this AngularUI Datepicker demo on Plunker: http://plnkr.co/edit/DWqgfTvM5QaO5Hs5dHco?p=preview I'm curious about how to store the selected value in a variable or trigger another function when a date is chosen in the field. I couldn't ...

How to achieve padding of strings in JavaScript or jQuery

Does anyone have information on a similar function in jQuery or JavaScript that mimics the prototype toPaddedString method? ...

When using ng-repeat with Angular ui-bootstrap and tabs, the new tab will not be selected if there are no existing tabs present

To showcase the problem I'm facing, please refer to this link: http://codepen.io/pietrofxq/pen/ZLLJdr?editors=1010 Click on "remove tabs" and then on "add tab" The challenge at hand involves using a loop with ng-repeat to display tabs. At times, the ...

Guide to building a nested React component

My custom dropdown component requires 2 props: trigger (to activate the dropdown) list (content to display in the dropdown) Below is the implementation of my component: import { useLayer } from "react-laag"; import { ReactElement, useState } fr ...

Generating a JSON file by combining data from two separate lists in order to render a visually appealing stacked bar chart

Combining two lists to create a JSON for generating a stacked bar chart using JavaScript list1 = ['2019-03-05', '2019-02-20', '2019-02-20', '2019-02-19', '2019-02-18', '2019-02-16', '2019-02 ...

What is the reason behind text underline being disabled when using "hidden: overflow" in CSS?

I have a long text string that I want to auto-shorten using CSS with text-overflow: ellipsis, but it's removing the underline from my link. Here is the code: NORMAL .link { font-weight: bold; cursor: pointer; color: black; text-decorat ...

Having trouble executing "npm install" following the clone from GitHub in React

After cloning a repository from GitHub, I attempted to run "npm install" but encountered the following error: Since the project is still in development, should I install or add anything else to successfully run it? ...

Prevent reloading the page when adding a flash element using JavaScript (jQuery)

Here is the function I am working with: function onVideo(vchat, idUser){ $('#videollamada').html('<div class="videollamada">'+ '<div align="right">'+ ...

Creating an anchor element with text and a bootstrap icon involves using the appropriate HTML structure and

I am trying to figure out how to create an element that includes both text and a Bootstrap icon. Here is the HTML code: <a> Edit <i class="icon-pencil" /> </a> Can anyone help me understand how to select this DOM element using jQuer ...

Attempting to retrieve an image from the database using ajax within a PHP script

I'm facing an issue with my code where I am attempting to retrieve an image from a database using AJAX, but it's not working as expected. Can someone please help me out? Image uploading works fine when trying to fetch the image using an anchor ta ...

Node.js: Extracting parameters from the URL

When working with Rails, I make a POST request to my server: response = Typhoeus::Request.post("http://url.localtunnel.com/request?from=ola&to=ole") result = JSON.parse(response.body) Now in my Node.js application, I need to retrieve values for From ...

Which programming language or technology utilizes the syntax <%VARIABLE%> and in what context is it employed?

First things first, I want to clarify that I'm not a developer, so this might be a simple question. Despite my research indicating that ASP typically uses this syntax, it's important to note that it is not the case here. I am currently delving i ...

The presence of 'touched' within Angular validation is causing a delay in method execution

Upon utilizing this validation method, it became apparent: <label>Password</label> <input type="password" formControlName="password" class="form-control" [ngClass]="{ 'is-invalid': f.password.touc ...

Looking to extract data from a Json object and add it into a table

<!DOCTYPE html> <html> <head> <script type="text/javascript"> function displayJsonData() { var jsonData = { "cars": [ '{"model":"Sentra", "doors":4, "features":["hi"," ...

Issue with Web Worker functionality in SvelteKit on Firefox version 106.0.5

I've set up a function in my main file like this: const loadWorker = async () => { const SyncWorker = await import("$lib/canvas.worker?worker"); syncWorker = new SyncWorker.default(); syncWorker?.postMessage({}); ...

The error message "connectDragSource is not a function" is being displayed in ReactDn

I'm currently working on a top-level component that comprises several different components. InventoryBox - marks the area where an inventory is located InventoryList - displays the list of items within the inventory Item - represents a single item w ...

Add the file retrieved from Firestore to an array using JavaScript

Trying to push an array retrieved from firestore, but encountering issues where the array appears undefined. Here is the code snippet in question: const temp = []; const reference = firestore.collection("users").doc(user?.uid); firestore .collec ...