logo@krutoo/utils

useVisualViewport

Page viewport size is 0 x 0Click to see

Hook for state of VisualViewport.

import { useVisualViewport } from '@krutoo/utils/react';

function App() {
  const viewport = useVisualViewport();

  return <div>Viewport width is {viewport.width}px</div>;
}

With addition to all numeric values from window.visualViewport you can use ready property. This property contains boolean indicates hook is successfully started observing window.visualViewport. It makes hook SSR ready.

If visualViewport is not supported in browser, ready will be false.

Stateless mode

If you want to observe visualViewport without re-render your component when it changes, you can provide mode option with stateless value.

This will result in the return value not being up to date so you can ignore it.

To listen changes you also can set callback to onChange option.

import { useRef } from 'react';
import { useVisualViewport } from '@krutoo/utils/react';

function App() {
  const ref = useRef<HTMLDivElement>(null);

  useVisualViewport({
    mode: 'stateless',
    onChange(viewport) {
      if (ref.current) {
        ref.current.textContent = `Viewport width is ${viewport.width}`;
      }
    },
  });

  return <div ref={ref}></div>;
}

VisualViewportContext

To define VisualViewport instance, hook uses VisualViewportContext under the hood. This context takes global instance from window.visualViewport by default.

But using global instance trough context gives you ability to replace implementation whenever you want.

It means that you can:

  • provide polyfill implementation for environments that don't support visualViewport;
  • provide implementation for SSR;
  • provide different implementations for different subtrees of your UI;
  • replace instance by mock in your unit tests.

For example next we will use VisualViewportMock:

import { VisualViewportContext, useVisualViewport } from '@krutoo/utils/react';
import { VisualViewportMock } from '@krutoo/utils/testing';
import { expect, render, test } from '#testing';

function MyWidget() {
  const viewport = useVisualViewport();

  return <div>Viewport width is {viewport.width}px</div>;
}

test('MyWidget should render message about width', () => {
  const mock = new VisualViewportMock({
    width: 1024,
    height: 768,
    scale: 1,
    offsetLeft: 0,
    offsetTop: 0,
    pageLeft: 0,
    pageTop: 0,
  });

  const context = {
    getVisualViewport: () => mock,
  };

  const { container } = render(
    <VisualViewportContext value={context}>
      <MyWidget />
    </VisualViewportContext>,
  );

  expect(container.textContent).toBe('Viewport width is 1024px');
});

This example shows that you don't need to use environment that implements VisualViewport during testing. So you can use jsdom for example.