Skip to content

Commit

Permalink
Merge pull request #622 from reach/rect-improvements
Browse files Browse the repository at this point in the history
rect: Fix issues with the observer between renders
  • Loading branch information
Chance Strickland committed Jun 22, 2020
2 parents 7eb3871 + a3ca034 commit 65938b5
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 48 deletions.
31 changes: 15 additions & 16 deletions packages/alert/src/index.tsx
Expand Up @@ -61,7 +61,7 @@ let renderTimer: number | null;
* @see Docs https://reacttraining.com/reach-ui/alert
*/
export const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
{ children, type = "polite", ...props },
{ children, type: regionType = "polite", ...props },
forwardedRef
) {
const ownRef = useRef(null);
Expand All @@ -75,7 +75,7 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
// eslint-disable-next-line react-hooks/exhaustive-deps
[children, props]
);
useMirrorEffects(type, child, ownRef);
useMirrorEffects(regionType, child, ownRef);

return child;
});
Expand Down Expand Up @@ -141,8 +141,8 @@ function renderAlerts() {
}
renderTimer = window.setTimeout(() => {
Object.keys(elements).forEach((elementType) => {
let type: RegionTypes = elementType as RegionTypes;
let container = liveRegions[type]!;
let regionType: RegionTypes = elementType as RegionTypes;
let container = liveRegions[regionType]!;
if (container) {
render(
<VisuallyHidden>
Expand All @@ -154,47 +154,46 @@ function renderAlerts() {
// will send out an accessible status event to assistive
// technology products which can then notify the user about it.
// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_status_role
role={type === "assertive" ? "alert" : "status"}
aria-live={type}
role={regionType === "assertive" ? "alert" : "status"}
aria-live={regionType}
>
{Object.keys(elements[type]).map((key) =>
React.cloneElement(elements[type][key], {
{Object.keys(elements[regionType]).map((key) =>
React.cloneElement(elements[regionType][key], {
key,
ref: null,
})
)}
</div>
</VisuallyHidden>,
liveRegions[type]
liveRegions[regionType]
);
}
});
}, 500);
}

function useMirrorEffects(
type: RegionTypes,
regionType: RegionTypes,
element: JSX.Element,
ref: React.RefObject<any>
) {
const prevType = usePrevious<RegionTypes>(type);
const prevType = usePrevious<RegionTypes>(regionType);
const mirror = useRef<Mirror | null>(null);
const mounted = useRef(false);
useEffect(() => {
const ownerDocument = getOwnerDocument(ref.current) || document;
if (!mounted.current) {
mounted.current = true;
mirror.current = createMirror(type, ownerDocument);
mirror.current = createMirror(regionType, ownerDocument);
mirror.current.mount(element);
} else if (prevType !== type) {
} else if (prevType !== regionType) {
mirror.current && mirror.current.unmount();
mirror.current = createMirror(type, ownerDocument);
mirror.current = createMirror(regionType, ownerDocument);
mirror.current.mount(element);
} else {
mirror.current && mirror.current.update(element);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [element, type, prevType]);
}, [element, regionType, prevType, ref]);

useEffect(() => {
return () => {
Expand Down
29 changes: 29 additions & 0 deletions packages/rect/examples/change-observed-ref.example.tsx
@@ -0,0 +1,29 @@
import React from "react";
import { useRect } from "@reach/rect";

let name = "Change the observed ref";

function Example() {
const refLeft = React.useRef<HTMLTextAreaElement>(null);
const refRight = React.useRef<HTMLTextAreaElement>(null);
const [whichRect, setWhichRect] = React.useState(true);
const rect = useRect(whichRect ? refLeft : refRight);
return (
<div>
<pre>
{whichRect ? "left" : "right"}: {JSON.stringify(rect, null, 2)}
</pre>
<button onClick={() => setWhichRect(!whichRect)}>
Toggle Observed Ref
</button>
<div>
<textarea ref={refLeft} defaultValue="resize this" />
<textarea ref={refRight} defaultValue="resize this" />
</div>
</div>
);
}

Example.story = { name };
export const Comp = Example;
export default { title: "Rect" };
2 changes: 1 addition & 1 deletion packages/rect/package.json
Expand Up @@ -13,7 +13,7 @@
"build": "ts-node ../../scripts/build"
},
"dependencies": {
"@reach/observe-rect": "1.1.0",
"@reach/observe-rect": "1.2.0",
"@reach/utils": "0.10.4",
"prop-types": "^15.7.2",
"tslib": "^2.0.0"
Expand Down
59 changes: 36 additions & 23 deletions packages/rect/src/index.tsx
Expand Up @@ -65,7 +65,10 @@ export type RectProps = {
*
* @see Docs https://reacttraining.com/reach-ui/rect#rect-onchange
*/
children(args: { rect: PRect | null; ref: React.Ref<any> }): JSX.Element;
children(args: {
rect: PRect | null;
ref: React.RefObject<any>;
}): JSX.Element;
};

if (__DEV__) {
Expand All @@ -86,48 +89,58 @@ if (__DEV__) {
* @param observe
* @param onChange
*/
export function useRect<T extends HTMLElement = HTMLElement>(
nodeRef: React.RefObject<T>,
export function useRect<T extends Element = HTMLElement>(
nodeRef: React.RefObject<T | undefined | null>,
observe: boolean = true,
onChange?: (rect: DOMRect) => void
): null | DOMRect {
let [element, setElement] = useState(nodeRef.current);
let initialRectSet = useRef(false);
let [rect, setRect] = useState<DOMRect | null>(null);
let observerRef = useRef<any>(null);
let onChangeRef = useRef<typeof onChange>();

useIsomorphicLayoutEffect(() => {
const cleanup = () => {
observerRef.current && observerRef.current.unobserve();
};
onChangeRef.current = onChange;
});

if (!nodeRef.current) {
console.warn("You need to place the ref");
return cleanup;
useIsomorphicLayoutEffect(() => {
if (nodeRef.current !== element) {
setElement(nodeRef.current);
}
});

if (!observerRef.current) {
observerRef.current = observeRect(nodeRef.current, (rect: DOMRect) => {
onChange && onChange(rect);
setRect(rect);
});
useIsomorphicLayoutEffect(() => {
if (element && !initialRectSet.current) {
initialRectSet.current = true;
setRect(element.getBoundingClientRect());
}
}, [element]);

if (!initialRectSet.current) {
initialRectSet.current = true;
setRect(nodeRef.current.getBoundingClientRect());
useIsomorphicLayoutEffect(() => {
let observer: ReturnType<typeof observeRect>;
if (!element) {
console.warn("You need to place the ref");
return cleanup;
}

observe && observerRef.current.observe();
observer = observeRect(element, (rect) => {
onChangeRef.current && onChangeRef.current(rect);
setRect(rect);
});

observe && observer.observe();
return cleanup;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [observe, onChange]);

function cleanup() {
observer && observer.unobserve();
}
}, [observe, element]);

return rect;
}

export default Rect;

export type PartialRect = Partial<PRect>;

export type PRect = Partial<DOMRect> & {
readonly bottom: number;
readonly height: number;
Expand Down
8 changes: 4 additions & 4 deletions packages/utils/src/index.tsx
Expand Up @@ -157,10 +157,10 @@ export function boolOrBoolString(value: any): value is "true" | true {
}

export function canUseDOM() {
return (
return !!(
typeof window !== "undefined" &&
typeof window.document !== "undefined" &&
typeof window.document.createElement !== "undefined"
window.document &&
window.document.createElement
);
}

Expand Down Expand Up @@ -615,7 +615,7 @@ export function useForkedRef<RefValueType = any>(
});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, refs);
}, [...refs]);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Expand Up @@ -3112,10 +3112,10 @@
dependencies:
"@types/node" ">= 8"

"@reach/observe-rect@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.1.0.tgz#4e967a93852b6004c3895d9ed8d4e5b41895afde"
integrity sha512-kE+jvoj/OyJV24C03VvLt5zclb9ArJi04wWXMMFwQvdZjdHoBlN4g0ZQFjyy/ejPF1Z/dpUD5dhRdBiUmIGZTA==
"@reach/observe-rect@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.2.0.tgz#d7a6013b8aafcc64c778a0ccb83355a11204d3b2"
integrity sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==

"@reach/router@^1.2.1", "@reach/router@^1.3.3":
version "1.3.3"
Expand Down

0 comments on commit 65938b5

Please sign in to comment.