From 8683f4d233d4da2cd1e5f8dfbe224685b285c106 Mon Sep 17 00:00:00 2001 From: Chalarangelo Date: Thu, 25 Nov 2021 11:01:40 +0200 Subject: [PATCH] Add usePortal --- snippets/useOnGlobalEvent.md | 2 +- snippets/useOnWindowResize.md | 2 +- snippets/useOnWindowScroll.md | 2 +- snippets/usePortal.md | 51 +++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 snippets/usePortal.md diff --git a/snippets/useOnGlobalEvent.md b/snippets/useOnGlobalEvent.md index b9a11da65..89e50a701 100644 --- a/snippets/useOnGlobalEvent.md +++ b/snippets/useOnGlobalEvent.md @@ -1,6 +1,6 @@ --- title: useOnGlobalEvent -tags: hooks,effect,intermediate +tags: hooks,effect,event,intermediate firstSeen: 2021-12-22T05:00:00-04:00 --- diff --git a/snippets/useOnWindowResize.md b/snippets/useOnWindowResize.md index c929f7011..4c6013007 100644 --- a/snippets/useOnWindowResize.md +++ b/snippets/useOnWindowResize.md @@ -1,6 +1,6 @@ --- title: useOnWindowResize -tags: hooks,effect,intermediate +tags: hooks,effect,event,intermediate firstSeen: 2021-12-01T05:00:00-04:00 --- diff --git a/snippets/useOnWindowScroll.md b/snippets/useOnWindowScroll.md index f31fb7756..f7d648c57 100644 --- a/snippets/useOnWindowScroll.md +++ b/snippets/useOnWindowScroll.md @@ -1,6 +1,6 @@ --- title: useOnWindowScroll -tags: hooks,effect,intermediate +tags: hooks,effect,event,intermediate firstSeen: 2021-12-08T05:00:00-04:00 --- diff --git a/snippets/usePortal.md b/snippets/usePortal.md new file mode 100644 index 000000000..6198350c6 --- /dev/null +++ b/snippets/usePortal.md @@ -0,0 +1,51 @@ +--- +title: usePortal +tags: hooks,state,effect,advanced +firstSeen: 2022-01-05T05:00:00-04:00 +--- + +Creates a portal, allowing rendering of children outside the parent component. + +- Use the `useState()` hook to create a state varible that holds the `render()` and `remove()` functions for the portal. +- Use `ReactDOM.createPortal()` and `ReactDOM.unmountComponentAtNode()` to create a portal and a function to remove it. Use the `useCallback()` hook to wrap and memoize these functions as `createPortal()`. +- Use the `useEffect()` hook to call `createPortal()` and update the state variable any time `el`'s value changes. +- Finally, return the `render()` function of the state variable. + +```jsx +const usePortal = el => { + const [portal, setPortal] = React.useState({ + render: () => null, + remove: () => null, + }); + + const createPortal = React.useCallback(el => { + const Portal = ({ children }) => ReactDOM.createPortal(children, el); + const remove = () => ReactDOM.unmountComponentAtNode(el); + return { render: Portal, remove }; + }, []); + + React.useEffect(() => { + if (el) portal.remove(); + const newPortal = createPortal(el); + setPortal(newPortal); + return () => newPortal.remove(el); + }, [el]); + + return portal.render; +}; +``` + +```jsx +const App = () => { + const Portal = usePortal(document.querySelector('title')); + + return ( +

+ Hello world! + Portalized Title +

+ ); +}; + +ReactDOM.render(, document.getElementById('root')); +```