Skip to content

Instantly share code, notes, and snippets.

@dimensi
Last active February 19, 2019 18:09
Show Gist options
  • Save dimensi/31013e435ec9edf15b81e4fc3bf757e5 to your computer and use it in GitHub Desktop.
Save dimensi/31013e435ec9edf15b81e4fc3bf757e5 to your computer and use it in GitHub Desktop.
React portal on context api
export default () => (
<PortalProvider>
<div>
<PortalTarget name="out" />
<div>
<div>
<h1>Content</h1>
<Portal to="out">
<p>text</p>
</Portal>
</div>
</div>
</div>
</PortalProvider>
)
import { useContext, useLayoutEffect } from 'react'
import { PortalContext } from './PortalContext'
export function Portal ({ to, children }) {
const { addTarget, removeTarget } = useContext(PortalContext)
useLayoutEffect(() => {
addTarget(to, children)
return () => {
removeTarget(to)
}
}, [to, children])
return null
}
import React from 'react'
export const PortalContext = React.createContext({
addTarget: () => {},
removeTarget: () => {},
targets: {},
})
import React, { useCallback, useState } from 'react'
import { PortalContext } from './PortalContext'
export function PortalProvider (props) {
const [targets, setTarget] = useState({})
const addTarget = useCallback(
(key, children) => {
setTarget(state => ({ ...state, [key]: children }))
},
[targets]
)
const removeTarget = useCallback(key => {
setTarget(state => {
const newState = { ...state }
delete newState[key]
return newState
})
})
return <PortalContext.Provider {...props} value={{ targets, addTarget, removeTarget }} />
}
import { useContext } from 'react'
import { PortalContext } from './PortalContext'
export function PortalTarget ({ name }) {
const { targets } = useContext(PortalContext)
return targets[name] || null
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment