When to query a containers width with Ionic React

I have been trying to figure out how to find the inner width of a container with Ionic React.

Normally in plain old React it would be as simple as using a ref and then. querying ref.current.offsetWidth in either componentDidMount or using useEffect with an empty dependency array as a couple of examples.

When I try to query ref.current.offsetWidth in a componentDidMount type context it always resolves to 0. I tried to set a timeout to see if this makes a difference and indeed it does. If I set a timeout for 700ms or more then I do get the width correctly. If it is any less than 700ms then it is 0. I can even visually see the element on the screen before this but the value of the ref width is 0. I appreciate processor speed etc may affect the time.

I sanity checked that I was not doing something silly by spinning up a standard create-react-app project. I pasted over my code and I got the ref immediately in componentDidMount.

@mhartington told me in this thread (that digressed) that I should use Ionic Lifecycle Hooks.

I tried the following:

  useIonViewWillEnter(() => {
    console.log("I am about to enter!",wrapperRef.current.offsetWidth);
  });

  useIonViewDidEnter(() => {
    console.log("I am entered!",wrapperRef.current.offsetWidth);
  });

Both resolve to 0 still, unless I use a timeout of couse.

Lastly, the effect I am experiencing is that when a Google Geochart has been rendered once, and navigated away from, then its width seems to collapse. I wonder if this is something in the Router animation?

Please see example:

On first load:

Ionic Router Cached version:

Any help would be greatly appreciated.

Can you put this into an example to test it out ?

Instead of a setTimeout, you could always use a requestAnimationFrame, which will be a bit more performant

Thanks @mhartington,

I will get something together and post it. I appreciate the support. I have a meeting with your team next week to discuss premium support options going forward.

Have a good weekend.

  useIonViewDidEnter(()=> {
      console.log(wrapperRef!.current!.offsetWidth);
  })

Use the life-cycle hook

Thanks Mike,

Are you seeing the issue with the map width collapsing once the page has been cached? I do not get this behaviour in create-react-app (tested). For some reason when Ionic is caching the page the map collapses its width.

You seem to have a big misunderstanding about what is going on in the app.

The caching has nothing to do with the map changes.

It’s related to the animations that happen and the chart constantly rerendering (over-rendering IMO).
You can see this by just resizing the browser window. So if the chart/map is constantly rerendering any change, any animations that are happening (which we do in Ionic) are going to affect the chart.

Basically, it seem like the chart needs to be added/removed from the DOM to stop it from over-rendering. Here’s a simplified version of what you could do.

import React, { useState } from 'react';
import {
  IonContent,
  IonHeader,
  IonPage,
  IonTitle,
  IonToolbar,
  IonButton,
  useIonViewWillEnter,
  useIonViewWillLeave,
} from '@ionic/react';
import './Home.css';
import Chart from 'react-google-charts';
import mapRegions from '../mapRegions';

const styles = {
  width: '100%',
  height: '100%',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
};
const Test: React.FC = () => {
  const [renderChart, setRenderChart] = useState(false);

  useIonViewWillEnter(() => {
    setRenderChart(true);
  });
  useIonViewWillLeave(() => {
    setRenderChart(false);
  });
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Blank</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent>
        <IonButton routerLink="/home">Test</IonButton>
        <div style={styles} ref={wrapperRef}>
          {renderChart ? <MyChart /> : null}
        </div>
      </IonContent>
    </IonPage>
  );
};
const MyChart = () => (
  <Chart
    width="100%"
    height="100%"
    chartType="GeoChart"
    data={mapRegions}
    options={{
      colorAxis: { colors: ['#ccc', 'orange'] },
      legend: 'none',
    }}
    mapsApiKey="******"
  />
);
export default Test;

Another option is to create your own chart component instead where you could control the rendering

1 Like

That is great.

Thanks for the tip.