From 2e5e88e127f5bac959aef7c0536b9d0cecd746ab Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 7 Apr 2019 10:34:44 -0700 Subject: [PATCH] Cleaned up is-overflowing hook and dependencies --- src/devtools/views/Components/OwnersStack.js | 65 ++++++-------------- src/devtools/views/hooks.js | 33 ++++++++++ 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/devtools/views/Components/OwnersStack.js b/src/devtools/views/Components/OwnersStack.js index e04bf18ce8..f3888c1817 100644 --- a/src/devtools/views/Components/OwnersStack.js +++ b/src/devtools/views/Components/OwnersStack.js @@ -7,12 +7,12 @@ import React, { createRef, forwardRef, } from 'react'; -import throttle from 'lodash.throttle'; import classNames from 'classnames'; import Button from '../Button'; import ButtonIcon from '../ButtonIcon'; import { TreeContext } from './TreeContext'; import { StoreContext } from '../context'; +import { useIsOverflowing } from '../hooks'; import type { Element } from './types'; @@ -27,6 +27,7 @@ function ElementsDropdown({ children, }: ElementsDropdownProps) { const [isDropdownVisible, setIsDropdownVisible] = useState(false); + const handleClick = useCallback(() => { setIsDropdownVisible(!isDropdownVisible); }, [isDropdownVisible, setIsDropdownVisible]); @@ -102,11 +103,11 @@ export default function OwnerStack() { const { ownerStack, ownerStackIndex, resetOwnerStack } = useContext( TreeContext ); - const [isElementsBarOverflowing, setIsElementsBarOverflowing] = useState( - false - ); + const [elementsTotalWidth, setElementsTotalWidth] = useState(0); const elementsBarRef = createRef(); + const isOverflowing = useIsOverflowing(elementsBarRef, elementsTotalWidth); + const elements = ownerStack.map((id, index) => ( )); @@ -115,22 +116,20 @@ export default function OwnerStack() { if (elementsBarRef.current === null) { return () => {}; } - const elements = Array.from(elementsBarRef.current.children); - const elementsTotalWidth = elements.reduce((acc, el) => { - const { offsetWidth } = el; - const marginRight = parseInt(getComputedStyle(el).marginRight, 10); - return acc + (offsetWidth + marginRight); - }, 0); + + let elementsTotalWidth = 0; + for (let i = 0; i < ownerStack.length; i++) { + const element = elementsBarRef.current.children[i]; + const computedStyle = getComputedStyle(element); + + elementsTotalWidth += + element.offsetWidth + + parseInt(computedStyle.marginLeft, 10) + + parseInt(computedStyle.marginRight, 10); + } setElementsTotalWidth(elementsTotalWidth); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ownerStackIndex, elementsBarRef]); - - useElementsBarOverflowing( - elementsBarRef, - elementsTotalWidth, - setIsElementsBarOverflowing - ); + }, [elementsBarRef, ownerStack.length]); return (
@@ -141,7 +140,7 @@ export default function OwnerStack() { > - {isElementsBarOverflowing && ( + {isOverflowing && ( {elements} @@ -149,35 +148,9 @@ export default function OwnerStack() {
); } - -function useElementsBarOverflowing( - elementsBarRef: Object, - elementsTotalWidth: number, - callback: Function -) { - useLayoutEffect(() => { - const handleResize = throttle(() => { - let isElementsBarOverflowing = false; - if (elementsBarRef.current !== null) { - const elementsBarWidth = elementsBarRef.current.clientWidth; - isElementsBarOverflowing = elementsBarWidth <= elementsTotalWidth; - } - callback(isElementsBarOverflowing); - }, 100); - - handleResize(); - - // It's important to listen to the ownerDocument.defaultView to support the browser extension. - // Here we use portals to render individual tabs (e.g. Profiler), - // and the root document might belong to a different window. - const ownerWindow = elementsBarRef.current.ownerDocument.defaultView; - ownerWindow.addEventListener('resize', handleResize); - return () => ownerWindow.removeEventListener('resize', handleResize); - }, [elementsBarRef, elementsTotalWidth, callback]); -} diff --git a/src/devtools/views/hooks.js b/src/devtools/views/hooks.js index 06364caa77..933a8097ba 100644 --- a/src/devtools/views/hooks.js +++ b/src/devtools/views/hooks.js @@ -1,7 +1,40 @@ // @flow +import throttle from 'lodash.throttle'; import { useCallback, useEffect, useLayoutEffect, useState } from 'react'; +export function useIsOverflowing( + containerRef: { current: HTMLDivElement | null }, + totalChildWidth: number +): boolean { + const [isOverflowing, setIsOverflowing] = useState(false); + + // It's important to use a layout effect, so that we avoid showing a flash of overflowed content. + useLayoutEffect(() => { + if (containerRef.current === null) { + return () => {}; + } + + const container = ((containerRef.current: any): HTMLDivElement); + + const handleResize = throttle( + () => setIsOverflowing(container.clientWidth <= totalChildWidth), + 100 + ); + + handleResize(); + + // It's important to listen to the ownerDocument.defaultView to support the browser extension. + // Here we use portals to render individual tabs (e.g. Profiler), + // and the root document might belong to a different window. + const ownerWindow = container.ownerDocument.defaultView; + ownerWindow.addEventListener('resize', handleResize); + return () => ownerWindow.removeEventListener('resize', handleResize); + }, [containerRef, totalChildWidth]); + + return isOverflowing; +} + // Forked from https://usehooks.com/useLocalStorage/ export function useLocalStorage( key: string,