How to Get The Size of a React Native View Dynamically
Sometimes you need to know the exact width or height of a View
in React Native dynamically at runtime. For instance, if you want to draw a chart scaled to the full page, you need to know the width and the height of the available space.
The Chart needs to know its width and height
There are two ways in React Native to dynamically get the size of a View
.
Using Dimensions
React Native provides a Dimensions
API that can be used to get the width and the height of the window. From there on, you can compute the available height yourself by subtracting the size of the Toolbar and the TabMenu.
import { Dimensions } from 'react-native'
class Test extends Component {
constructor (props) {
super(props)
let {width, height} = Dimensions.get('window')
console.log(width, height)
}
}
This is quick and easy, but the drawback of this approach is that you need to know the size of the Views
above and below the one you want to render. Another confusing thing is that the height received this way includes the height of the Soft Buttons Menu Bar on Android, which you usually want to exclude as you don’t want to draw behind it. Therefore, I prefer using the second method.
Using onLayout
Each View
has an onLayout(event)
callback function receiving an event that includes the View's
x, y, width
and height
properties. The problem is that this function is only called after the first render
call, but the whole point was to know the size before rendering to know how much space is available for the component. The solution I use is to render a dummy View first, wait for onLayout
to get called, extract the width and height, and then set the state to trigger a re-render. In the second render the actual View with its contents can now be rendered instead of the dummy View. One must make sure that the dummy View with no contents has the same size as the real View with the actual contents. For this we can use React Native’s flexbox layout to stretch the empty dummy View to all the available space. The code looks like this:
import React, { Component, PropTypes } from 'react'
import { View } from 'react-native'
import Svg from 'react-native-svg'
export default class LineChart extends Component {
constructor (props) {
super(props)
this.state = {dimensions: undefined}
}
render () {
// If dimensions is defined, render the real view otherwise the dummy view
if (this.state.dimensions) {
var { dimensions } = this.state
var { width, height } = dimensions
// do stuff
...
}
return (
<View style={{flex: 1, alignSelf: 'stretch'}} onLayout={this.onLayout}>
{
this.state.dimensions
? <Svg width={width} height={height}>
...
</Svg>
: undefined}
</View>
)
}
onLayout = event => {
if (this.state.dimensions) return // layout was already called
let {width, height} = event.nativeEvent.layout
this.setState({dimensions: {width, height}})
}
}