Forcing a single re-render to measure DOM elements? - javascript

I'm developing a numerical Slider component in React. It actually works right now and is fully functional, but there is a single issue. I set the width of the thumb and total slider via CSS, and I like doing it that way, as it's flexible.
The slider and thumb width is also needed for calculations, such as calculating the max position of the slider, or the position of the thumb relative to the absolute.
So, I have functions like this:
getSliderWidth = () => {
return this.sliderRef.current ? this.sliderRef.current.offsetWidth : 0;
}
getThumbWidth = () => {
return this.sliderThumbRef.current ? this.sliderThumbRef.current.clientWidth : 0;
}
Where the refs are defined in the render function like so:
<div ref={this.sliderRef} className="slider-range">
<div className="slider-range-connect" style={connectStyle}></div>
<div ref={this.sliderThumbRef} className="slider-range-thumb" style={thumbStyle}></div>
</div>
The issue with this, though, is that the widths are only known after the first render. The refs aren't available until it is first rendered. So, I think what I need to do is force a re-render, such that when the component is initially loaded, it is rendered one more time. This is because the initial render function needs to know the relative max position and thumb position in order to render the initial elements.
So, is there a "React" way to do this, or am I missing something? Is the ideal solution here to have some sort of boolean flag, and in the initial mount function, to check this flag, and if it is the first render, to do another render, and then to set the flag? That doesn't seem very "React"ish, so I was wondering if there is something more elegant.

Related

How can re-render react-grid-layout column's width on parent's resizing?

I have a dashboard panel using react js that I used react-grid-layout in it; I used react-grid-layout in my content and that works correctly but when I close my right side or left side panels (drawers) , the width of my content that is the parent of my react-grid-layout modifies but after this changes my column's width did not modify according to their parent's width size; How can I reload my ResponsiveGridLayout component to changing the childs(columns) width?
this is simple code for showing my problem in this question :
example
this is my dashboard image :
If you look at the documentation here you'll see that ResponsiveGridLayout is a combination of a component called Responsive and a HOC called WidthProvider
import { Responsive, WidthProvider } from 'react-grid-layout';
const ResponsiveGridLayout = WidthProvider(Responsive);
If you look at the code of WidthProvider you may notice it subscribes to widnow resize event
window.addEventListener("resize", this.onWindowResize);
which isn't particularly useful for you, because in your case window does not resize. One way to deal with this problem is to create your own WidthProvider and attach the listener to the element that actually resizes and wrap it around Responsive
const MyVeryOwnResponsiveGridLayout = MyWidthProvider(Responsive);
P.S. You can try requesting the feature from the component creator or volunteer to contribute to the project
I found a better answer than extending the WidthProvider:
Trigger a Window Resize event (without actually resizing the window). I found a delay of 1ms is necessary to make it work.
See this solution:
https://stackoverflow.com/a/62874552/17570516

React: How to get the ref height of an element coming from the children with dynamic height?

I have a component that has some element with text.
Based on some other stuff the text can be some words or can be a wall of text. From the parent I need to know the exact height at any time of the element inside of the children
I have tried using callback refs but to no avails. I can only get the first value but then I need it to update
https://codesandbox.io/s/nervous-mendeleev-xf2cg?file=/src/App.js
Here is an example of the code.
Update: one thing that I notice when I check directly the height is that I get the previous value (kinda like when you use setState and dont realize it is asynchronous)
when small ( height: ~60) it shows ~100
when big the other way around
one thing that I notice when I check directly the height is that I get
the previous value
You can still salvage this solution. It is getting the previous value because you are checking for the height right away but the issue is that the component is not done rendering using the new words (i.e., the ref height would still return the previous height).
What you can do is to leverage useEffect. When the component is done rendering (equivalent of this in class implementation is componentDidUpdate), that is when you start checking its height.
useEffect(() => {
// the component is done rendering - now you can check for its new height here
}, [words, onClick]);

Use key props to force re-render

I have a component where there are draggable components inside with translate applied for the drag&drop and a default translation when it's rendered depending on parent's height and width to keep the ratio.
When I resize the window, the parent component change size so the components inside have to re-render to reset the default translation based on the parent size. I pass the size props to the child's but they do not rerender, I believe this is because the translation is done straight in the style attribute.
So I made a trick to force re-render, I pass the props key to the wrapper where the value is the height multiplicate by a width which makes a unique key, change on resizing and then rerender my components with the right default position. Here is what it looks like :
render() {
const { height, width, frame, elem } = this.state;
const key = height * width;
return (
<WrapperFrame>
<WrapperMediaElem key={frame.id}>
<WrapperElements key={key} className="droppable" size={{height, width}}>
<DraggableElement element={{...elem}} size={{height, width}} />
</WrapperElements>
</WrapperMediaElem>
</WrapperFrame>
);
}
This solution works for me but it looks like a "trick" and maybe not the good approach. I wanted to know if it's the right way or if is there a better solution?

Where to set the height of a component in the lifecycle?

I have a problem where I am using a React grid component in order to display multiple charts. If you imagine the webpage is a 4 x 4 grid and I have two charts. Each chart being a 4 x 2 grid item. The grid library uses row height in order to say how large (y-axis) the charts are - so imagine if the row height is statically set to 100px, then the gird is going to be a width x 400px grid.
The problem comes in because being made for different displays or accounting for if someone is going to have the application minimized (not full screen) the height of the component the charts will be rendered in is variable. I am able to get the height of the component's <div /> by using:
return (
<div
className="content absolute top right left bottom"
ref={divElement => this.divElement = divElement}
>
...
and then calling setState in the componentDidMount lifecycle method.
componentDidMount() {
const height = this.divElement.clientHeight
this.setState({ height })
}
I can get the height of the <div /> fine with this approach but the obvious caveat to this aside from eslint complaining is that it will cause a re-render of the component which is bad.
My question is how should I approach this if there is a better way to complete this same task? Is there another lifecycle method this would be better in (not really thinking there is). It's just that I have to wait until the <div /> the charts reside in to be rendered before I can say how big the charts should be

How get the horizontal-size from the DOM?

I have a complex web page using React components, and am trying to convert the page from a static layout to a more responsive, resizable layout. However, I keep running into limitations with React, and am wondering if there's a standard pattern for handling these issues. In my specific case, I have a component that renders as a div with display:table-cell and width:auto.
Unfortunately, I cannot query with width of my component, because you can't compute the size of an element unless it's actually placed in the DOM (which has the full context with which to deduce the actual rendered width). Besides using this for things like relative mouse positioning, I also need this to properly set width attributes on SVG elements within the component.
In addition, when the window resizes, how do I communicate size changes from one component to another during setup? We're doing all of our 3rd-party SVG rendering in shouldComponentUpdate, but you cannot set state or properties on yourself or other child components within that method.
Is there a standard way of dealing with this problem using React?
The method of lifecycle you probably want is componentDidMount
The elements have already been placed in the DOM and you can get information about them from the component's refs.
Example:
var Container = React.createComponent({
componentDidMount: function () {
var width = this.refs.svg.getDOMNode().offsetWidth;
},
render: function () {
<svg ref="svg" />
}
});
In componentDidMount you can get the window.width or ReactDom.findDOMNode(this).width. Then use the width in state and pass as a prop as needed. You can also set a listener for the resize event.

Categories

Resources