diff --git a/CHANGELOG.md b/CHANGELOG.md index 231e242a658..07f9a112f0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Increased column width on `EuiTableHeaderCellCheckbox` to prevent `EuiCheckbox`'s focus ring from getting clipped in `EuiBasicTable` ([#2770](https://github.com/elastic/eui/pull/2770)) - Fixed the display of `EuiButton` within `EuiControlBar` when `fill={true}` to be more consistent with other buttons ([#2781](https://github.com/elastic/eui/pull/2781)) - Fixed `EuiFormControlLayout` from overwriting className for `prepend` nodes. ([#2796](https://github.com/elastic/eui/pull/2796)) +- Fixed `useRenderToText` and `EuiButtonToggle` from attempting state updates on unmounted components ([#2797](https://github.com/elastic/eui/pull/2797)) **Deprecations** diff --git a/src/components/inner_text/render_to_text.tsx b/src/components/inner_text/render_to_text.tsx index 33ec51d591d..07f1e14135d 100644 --- a/src/components/inner_text/render_to_text.tsx +++ b/src/components/inner_text/render_to_text.tsx @@ -1,21 +1,34 @@ -import React, { ReactNode, useEffect } from 'react'; +import React, { ReactNode, useCallback, useEffect, useRef } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { useInnerText } from './inner_text'; export function useRenderToText(node: ReactNode, placeholder = ''): string { const [ref, text] = useInnerText(placeholder); + const hostNode = useRef(null); + + const onUnmount = () => { + if (hostNode.current) { + unmountComponentAtNode(hostNode.current); + hostNode.current = null; + } + }; + + const setRef = useCallback( + (node: Element | null) => { + if (hostNode.current) { + ref(node); + } + }, + [ref] + ); useEffect(() => { - const hostNode = (document.createDocumentFragment() as unknown) as Element; - render(
{node}
, hostNode); + hostNode.current = (document.createDocumentFragment() as unknown) as Element; + render(
{node}
, hostNode.current); return () => { - // since we're in React's lifecycle via `useEffect`, wait a - // tick to escape otherwise React performs multiple unmounts 🤷 - requestAnimationFrame(() => { - unmountComponentAtNode(hostNode); - }); + onUnmount(); }; - }, [node, ref]); + }, [node, setRef]); return text || placeholder; }