React DOM's offsetHeight prior to rendering

I am struggling to figure out how to position a React DOM element based on its offsetHeight.

The issue is that I cannot determine the offsetHeight of an element that hasn't been created yet (so the height cannot be passed as a parameter to the render function) and calculating the height inside the render function goes against the guidelines in the React DOM refs documentation:

Avoid accessing refs within a component's render method or while any render method is running elsewhere in the call stack.

The DOM element needs to be rendered relative to an icon that, when clicked, displays it.

The component tree structure is as follows:

|— FormInputGroup
  |— Label
    |— TooltipIcon
  |— Tooltip
  |— Input

The render function for TooltipIcon:

const TooltipIcon = ({ attribute, setCurrentTooltip }) => (
  <Icon
    name="tooltip"
    style={{
      position: "relative",
      top: "1px",
      marginLeft: "10px",
      cursor: "pointer",
    }}
    onClick={() => setCurrentTooltip(attribute)}
  />
)

The render function for Tooltip:

const Tooltip = ({ title, content, setCurrentTooltip }) => (
  <div className="popover"
    style={{
//      top: "-"+ ReactDOM.findDOMNode(this).offsetHeight +"px",
    }}
  >
    <h3 className="popover-title">{title}</h3>
    <div className="popover-close-button"
      onClick={() => setCurrentTooltip(null)}
    />
    <div className="popover-content">{content}</div>
  </div>
)

Here is the current state of the DOM element without proper positioning: not positioned

This is how I envision the desired rendering with top: -(offsetHeight)px: positioned

Answer №1

If you're not sure about the most effective solution, one approach is to manage your style using component state. Begin by initializing it as empty:

this.state = { style: {} };

Then, update this state within a componentDidMount() function after successfully accessing the target element:

componentDidMount() {
  this.setState(
    {style: { top: "-" + (ReactDOM.findDOMNode(this).offsetHeight) + "px" } }
  );
} 

You can pass this state down as a prop to any child components that require it.

I've created a codepen with explanatory comments for further clarity: http://codepen.io/anon/pen/vGwyVj

Answer №2

I encountered a similar issue before, and I found a solution by utilizing the lifecycle method componentDidMount. This approach requires using a controlled or 'smart' component.

LIVE EXAMPLE

class Popover extends React.component {
  constructor(props) {
    super(props);
    this.state = {
      mounted: false,
      // other state properties
    };
  }
  ....
}

Within the componentDidMount method, you can update the state of mounted to true. This is also where you can access your component:

componentDidMount() {
  this.setState({mounted: true})
  // apply necessary styles here...
}

Subsequently, in the render function, you can conditionally display the component based on its mount status:

render() {
   if (this.state.mounted) {
     // return the component with offset style
     // which can be passed as a prop...
   } else {
     // present a version of the component without offset if needed, 
     // personally I find React's efficiency sufficient. 
     }
}

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

How can we ensure that the component updates when React router params change and force a remount

I've created a basic application that fetches remote resources within the componentDidMount methods of the components. Using React Router, I notice that when the route completely changes, the previous component unmounts properly before the new one mo ...

The custom font in my Next.js project is functioning perfectly in the development environment, but when I try to build it locally, the font doesn

Incorporating the Amazon Ember font as a custom font in my application has been a bit of a challenge. I followed the necessary steps of setting up a font.ts file in the lib folder and exporting the font correctly, as guided by various blog posts. Initially ...

Looking for assistance with transferring a data attribute to a form redirection

I'm seeking assistance with a coding dilemma I've encountered. To provide some background, I have a list of items on my website, each featuring a 'Book Now' button that redirects users to different pages. Recently, I incorporated a mod ...

The styling of Bootstrap CSS is not displaying correctly within the body of the webpage

I'm facing an issue where the content in the body of my page is not rendering correctly due to a problem with my bootstrap css. Specifically, the paragraph tag seems to be rendering inside the image tag even though both are within the body. @{ Lay ...

Setting the base URL in Next.js according to an environment variable: a step-by-step guide

I currently have a Strapi backend and Next.js frontend application deployed on DigitalOcean. Within DigitalOcean, I have configured an environment variable for the frontend: API_URL = ${APP_URL}/api To generate the base URL, I retrieve this variable using ...

My setup seems to be causing all of my TRPC queries to fail with a 500 error. Can anyone help me troub

Having recently started using TRPC, I've implemented a custom hook in my NextJS app for making queries. The issue I'm facing is that this hook sends a query to generateRandomWorker, but the response always results in a generic 500 error. Solving ...

Utilize Material icons in CSS for a list in Angular 9

My task involves altering the HTML provided by a content management system for one of our applications. Specifically, I need to replace all "ul"s with <mat-icon>check_circle_outline</mat-icon> instead of the default "." The challenge lies in t ...

Ways to retrieve information and functions from defineExpose in Vue

`I am attempting to call a method from a child component within the parent component in Vue using the composition API. I have defined the method inside defineExpose, but I am unable to access it in the parent component. Hero.vue (ChildComponent) <templ ...

Unable to refresh the context following a successful API call

My current project in NextJS requires a simple login function, and I have been attempting to implement it using the Context API to store user data. However, I am facing an issue where the context is not updating properly after fetching data from the back-e ...

Tips for resolving the "trim" of undefined property error in Node.js

Looking to set up a basic WebAPI using Firebase cloud functions with express and TypeScript. Here's the code I have so far: import * as functions from 'firebase-functions'; import * as express from 'express'; const app = express( ...

The presence of element a within the element ul is not permitted in this particular scenario. Additional errors from this section will not be displayed

Having trouble with this code snippet: <nav role="navigation"> <div id="menuToggle"> <input type="checkbox"/> <span></span> <span></span> <span></span> ...

An interactive selection tool for a website's navigation bar

Struggling to implement a dropdown menu for my navbar, none of the CSS methods I've tried seem to work. My current HTML code is as follows... <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-t ...

Do not need to refresh the form

I developed a PHP-based Feedback form that includes a Popup from Foundation 5. <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <style type="text/css"> .auto-style1 { margin-left: 1px; ...

Looking to implement a delay in the model popup using Pure JavaScript

Looking for a method to implement a delay in the popup... I've tried a few solutions without success. What's the secret to adding a delay to the modal below? Any help is greatly appreciated. var modal = document.querySelector(".modal"); var t ...

Supertest and Jest do not allow for sending JSON payloads between requests

Below is the test function I have written: describe("Test to Create a Problem", () => { describe("Create a problem with valid input data", () => { it("Should successfully create a problem", async () => { const ProblemData = { ...

Modifying input values in ANTD by blur event on another input

I am currently using ANTD 4.1.2V and I have an input field within a form that accepts a Zip code number. Upon triggering the onblur event, I make a call to an API with the Zip code value to retrieve address information. The objective is to automatically ...

What is the best way to implement client side validation for a related input field using JavaScript in a Rails application?

Struggling to find a solution for adding a validation check for a dropdown list that is dependent on another input selection in a different form. It seems like database validation may be necessary, but I'm unsure of the best approach. Any suggestions ...

Create an interface object in TypeScript with no initial properties

I'm currently developing an app with angular 5 (TS) and I've encountered a problem while trying to initialize an empty object of my interface. The solution that I found and attempted is as follows: article: Article = {} as Article; However, ...

Notify the chat when a new record is added to the database

Alright, so here's the issue I'm facing. I created a chat application using PHP and MySQL, but I noticed that when I enter text, it doesn't update in the other window automatically. This led me to experiment with iframes, which I found much ...

Having trouble retrieving my .scss bundle file from a different directory

I am trying to access my .scss file that imports all the other .scss files (they are all imported in styles.scss). Even though I have added the path to my angular cli file, the file is still not accessible and no styling is being applied to my html elemen ...