React: How to get Parent component's width from Child component with hooks?
in order to access ref in your child component, you need to wrap you component in a React.forwardRef function & use the ref as the second argument & not inside the props object so:
const Child = React.forwardRef((props, ref) => {})
& in your parent you would have:
<Child ref={parentRef}/>
you can read more about it here
How to get a react component's size (height/width) before render?
As it was already mentioned, you can't get any element's dimensions until it is rendered to DOM. What you can do in React is to render only a container element, then get it's size in componentDidMount
, and then render rest of the content.
I made a working example.
Please note that using setState
in componentDidMount
is an anti-pattern but in this case is fine, as it is exactly what are we trying to achieve.
Cheers!
Code:
import React, { Component } from 'react';
export default class Example extends Component {
state = {
dimensions: null,
};
componentDidMount() {
this.setState({
dimensions: {
width: this.container.offsetWidth,
height: this.container.offsetHeight,
},
});
}
renderContent() {
const { dimensions } = this.state;
return (
<div>
width: {dimensions.width}
<br />
height: {dimensions.height}
</div>
);
}
render() {
const { dimensions } = this.state;
return (
<div className="Hello" ref={el => (this.container = el)}>
{dimensions && this.renderContent()}
</div>
);
}
}
How to measure the height of a child and pass it to a parent in React?
Personally, I would call the hook in the Main
component and wrap the child component in a forwardRef
(check docs here).
See full example here:
Main.tsx
export default function Main(props: { notifications: Notification[] }) {
const { notifications } = props;
const [ref, dimensions] = useDimensions()
return (
<main>
<Header ref={ref}/>
{JSON.stringify(dimensions)}
</main>
)
}
What's done here, we just pass the ref down the tree to the child component and we just show the dimensions (testing purposes).
DefaultHeader.tsx
import { forwardRef } from "react";
const DefaultHeader = forwardRef((_, ref) => {
return (
<>
<div ref={ref} className="header" >
<h1>Hello!</h1>
</div>
</>
);
});
export default DefaultHeader;
Here, we just attach the ref to the container that it has previously been (your example).
See full example on this CodeSandbox.
Let me know if you need more explanations on this.
React get size of parent element within ResponsiveReactGridLayout
Property 'width' does not exist on type 'never'
This is actually a TypeScript error. It means that TypeScript doesn't know what the type of the .current
property is supposed to be. Therefore it doesn't know that .current
has properties .width
and .height
and it prevents you from accessing them. You need to tell TypeScript that this is a ref to a div
.
An HTMLDivElement
doesn't actually have .width
and .height
, but you can use .clientWidth
or .offsetWidth
instead.
const canvasRef = useRef<HTMLDivElement>(null);
I've tried using the useRef() hook, but it always seems to have current as null.
The ResponsiveReactGridLayout
component is setting its own refs on its children, so your ref={canvasRef}
gets overwritten.
The simplest way around this is to add another layer of nesting. The ResponsiveReactGridLayout
will set a ref on the outermost div
, but you can add another div
inside of it with a ref that you control. Make sure that it fills up the entire height.
Note that the .current
property might still be null
on the first render. You can use a default value, as suggested by @PushpikaWan, or you can delay rendering the chart until you have an actual width.
<ResponsiveReactGridLayout /*...props...*/ >
<div key="candlestick" className="box">
<div ref={canvasRef} style={{ width: "100%", height: "100%" }}>
{canvasRef.current ? (
<Candlestick
data={data}
width={canvasRef.current.clientWidth}
height={canvasRef.current.offsetHeight}
/>
) : null}
</div>
</div>
/*...other children...*/
</ResponsiveReactGridLayout>
I needed to add an explicit width
on the grid layout in order to get this to work. I'm not sure if you are doing this part already, but you can use the WidthProvider
as explained in the docs.
import { Responsive, WidthProvider } from 'react-grid-layout';
const ResponsiveReactGridLayout = WidthProvider(Responsive);
CodeSandbox Demo
How to get size of an element in an react app that is totally based on functional components?
Instead of using class component with componentDidMount
there is a hook called useEffect
which can help you to catch somehow the rendered state of the component.
The solution to get height of a rendered DOM element can be the following with functional component:
const YourComponent = () => {
const inputRef = useRef(null);
useEffect(() => {
const height = inputRef.current.offsetHeight;
console.log('Input height', height);
}, [inputRef]);
return <>
<input ref={inputRef} type="text" defaultValue="testing" />
</>
}
Explanation:
useRef
hook will help you to keep the reference for your object for the life of your component, as the documentation states:
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
Once you use useEffect
combined with useRef
just like in the above solution you will get the expected result. For useEffect
hook the documentation explains:
The function passed to useEffect will run after the render is committed to the screen.
Passing inputRef
to the dependency array will trigger the passed function on change of the input
element for useEffect
. Then the code calculates the height of your component which in this case just an input
element. This can be any DOM element, just like a div
.
UPDATE:
From your updated question, the solution is forwardRef
, read in the documentation:
Ref forwarding is a technique for automatically passing a ref through a component to one of its children.
With this technique you can access the child component's properties in the parent component e.g.: height of an internal <div>
or <input>
element what the code can use for further purposes.
In your child functional component, the code should use forwardRef
in order to access internal DOM elements, just like below:
import React, { forwardRef } from 'react';
// ...
const YourComponent = forwardRef((props, ref) => {
return <>
<input ref={ref} type="text" defaultValue="testing" />
</>
});
Then use in the parent component as the following:
const componentRef = useRef(null);
useEffect(() => {
const {offsetHeight} = componentRef.current;
console.log('componentRef height', offsetHeight);
}, [componentRef]);
return <>
<YourComponent ref={componentRef} />
</>
If you are interested further then please find the documentation here:
Forwarding refs to DOM components
Please find a working GitHub solution for your scenario what I just built up for representation:
norbitrial/react-forwarding-ref-example
Hope this gives you the idea to proceed further.
In React, how can I calculate the width of a parent based on it's child components?
Sounds like a use case for ResizeObserver
API.
function useResizeObserver() {
const [size, setSize] = useState({ width: 0, height: 0 });
const resizeObserver = useRef(null);
const onResize = useCallback((entries) => {
const { width, height } = entries[0].contentRect;
setSize({ width, height });
}, []);
const ref = useCallback(
(node) => {
if (node !== null) {
if (resizeObserver.current) {
resizeObserver.current.disconnect();
}
resizeObserver.current = new ResizeObserver(onResize);
resizeObserver.current.observe(node);
}
},
[onResize]
);
useEffect(
() => () => {
resizeObserver.current.disconnect();
},
[]
);
return { ref, width: size.width, height: size.height };
}
ReactJS - Get Height of an element
See this fiddle (actually updated your's)
You need to hook into componentDidMount
which is run after render method. There, you get actual height of element.
var DivSize = React.createClass({
getInitialState() {
return { state: 0 };
},
componentDidMount() {
const height = document.getElementById('container').clientHeight;
this.setState({ height });
},
render: function() {
return (
<div className="test">
Size: <b>{this.state.height}px</b> but it should be 18px after the render
</div>
);
}
});
ReactDOM.render(
<DivSize />,
document.getElementById('container')
);
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div id="container">
<p>
jnknwqkjnkj<br>
jhiwhiw (this is 36px height)
</p>
<!-- This element's contents will be replaced with your component. -->
</div>
Related Topics
How to Change Font-Size in Vuetify Component
What Is the Purpose of the Dollar Sign in JavaScript
How to Prevent Invalid Characters from Being Typed into Input Fields
React Typescript Is Not Assignable to Parameter of Type
How to Wait Until 2 $Http Requests End in Angularjs
If a Div Contains a Specific String of Text, Edit the Parent'S CSS
How to Filter Date Range in Datatables
Setting Cross-Domain Cookies in Safari
Typescript Module Has No Exported Members - React
Textarea With Initial Auto Height by Content With Pure CSS
Howto Trigger Input Event Programmatically on Input Text Field
Javascript: Refresh Same Page With Query String from Selects
Prevent Form Redirect or Refresh on Submit
Change HTML Code Without Refreshing the Page
Preventing Page from Scrolling on Focus Switching
Want to Show/Hide Div Based on Dropdown Box Selection
Reverse Strings Without Changing the Order of Words in a Sentence