Skip to content

Commit

Permalink
[add] useWindowDimensions hook
Browse files Browse the repository at this point in the history
Add the 'useWindowDimensions' hook from React Native

Close #1487
  • Loading branch information
MoOx authored and necolas committed Dec 18, 2019
1 parent 4763cc7 commit df1b62c
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 12 deletions.
3 changes: 2 additions & 1 deletion packages/babel-plugin-react-native-web/src/moduleMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@ module.exports = {
findNodeHandle: true,
processColor: true,
render: true,
unmountComponentAtNode: true
unmountComponentAtNode: true,
useWindowDimensions: true
};
6 changes: 4 additions & 2 deletions packages/react-native-web/src/exports/DeviceInfo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
* @flow
*/

import type { DisplayMetrics } from '../Dimensions';

import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import Dimensions from '../Dimensions';

const DeviceInfo = {
Dimensions: {
get windowPhysicalPixels() {
get windowPhysicalPixels(): DisplayMetrics {
const { width, height, fontScale, scale } = Dimensions.get('window');
return {
width: width * scale,
Expand All @@ -21,7 +23,7 @@ const DeviceInfo = {
fontScale
};
},
get screenPhysicalPixels() {
get screenPhysicalPixels(): DisplayMetrics {
const { width, height, fontScale, scale } = Dimensions.get('screen');
return {
width: width * scale,
Expand Down
38 changes: 30 additions & 8 deletions packages/react-native-web/src/exports/Dimensions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,44 @@ import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import debounce from 'debounce';
import invariant from 'fbjs/lib/invariant';

export type DisplayMetrics = {|
fontScale: number,
height: number,
scale: number,
width: number
|};

type DimensionsValue = {|
window?: DisplayMetrics,
screen?: DisplayMetrics
|};

type DimensionKey = 'window' | 'screen';

type DimensionEventListenerType = 'change';

const win = canUseDOM
? window
: {
devicePixelRatio: undefined,
innerHeight: undefined,
innerWidth: undefined,
innerHeight: (undefined: any),
innerWidth: (undefined: any),
screen: {
height: undefined,
width: undefined
height: (undefined: any),
width: (undefined: any)
}
};

const dimensions = {};
const listeners = {};

export default class Dimensions {
static get(dimension: string): Object {
static get(dimension: DimensionKey): DisplayMetrics {
invariant(dimensions[dimension], `No dimension set for key ${dimension}`);
return dimensions[dimension];
}

static set(initialDimensions: ?{ [key: string]: any }): void {
static set(initialDimensions: ?DimensionsValue): void {
if (initialDimensions) {
if (canUseDOM) {
invariant(false, 'Dimensions cannot be set in the browser');
Expand Down Expand Up @@ -64,12 +80,18 @@ export default class Dimensions {
}
}

static addEventListener(type: string, handler: Function): void {
static addEventListener(
type: DimensionEventListenerType,
handler: DimensionsValue => void
): void {
listeners[type] = listeners[type] || [];
listeners[type].push(handler);
}

static removeEventListener(type: string, handler: Function): void {
static removeEventListener(
type: DimensionEventListenerType,
handler: DimensionsValue => void
): void {
if (Array.isArray(listeners[type])) {
listeners[type] = listeners[type].filter(_handler => _handler !== handler);
}
Expand Down
35 changes: 35 additions & 0 deletions packages/react-native-web/src/exports/useWindowDimensions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/

'use strict';

import type { DisplayMetrics } from '../Dimensions';

import Dimensions from '../Dimensions';
import { useEffect, useState } from 'react';

export default function useWindowDimensions(): DisplayMetrics {
const [dims, setDims] = useState(() => Dimensions.get('window'));
useEffect(() => {
function handleChange({ window }) {
// $FlowFixMe
setDims(window);
}
Dimensions.addEventListener('change', handleChange);
// We might have missed an update between calling `get` in render and
// `addEventListener` in this handler, so we set it here. If there was
// no change, React will filter out this update as a no-op.
setDims(Dimensions.get('window'));
return () => {
Dimensions.removeEventListener('change', handleChange);
};
}, []);
return dims;
}
7 changes: 6 additions & 1 deletion packages/react-native-web/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ import TVEventHandler from './exports/TVEventHandler';
// plugins
import DeviceEventEmitter from './exports/DeviceEventEmitter';

// hooks
import useWindowDimensions from './exports/useWindowDimensions';

export {
// top-level API
createElement as unstable_createElement,
Expand Down Expand Up @@ -145,5 +148,7 @@ export {
TimePickerAndroid,
TVEventHandler,
// plugins
DeviceEventEmitter
DeviceEventEmitter,
// hooks
useWindowDimensions
};

0 comments on commit df1b62c

Please sign in to comment.