Last active
September 21, 2022 13:20
-
-
Save CodyJasonBennett/340427c56a076b1bde6344fa5ce48773 to your computer and use it in GitHub Desktop.
R3F cross-container context
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from 'react' | |
import * as ReactDOM from 'react-dom/client' | |
import { render } from 'react-nil' | |
function traverseFiber(fiber, ascending, selector) { | |
if (selector(fiber) === true) return fiber | |
let child = ascending ? fiber.return : fiber.child | |
while (child) { | |
const match = traverseFiber(child, ascending, selector) | |
if (match) return match | |
child = child.sibling | |
} | |
} | |
const contexts = [] | |
function useContextBridge(fiber) { | |
if (fiber) { | |
traverseFiber(fiber, true, (node) => { | |
const context = node.type?._context | |
if (context && !contexts.includes(context)) contexts.push(context) | |
}) | |
} | |
return contexts.reduce( | |
(Prev, context) => { | |
const value = | |
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current.readContext(context) | |
return (props) => ( | |
<Prev> | |
<context.Provider {...props} value={value} /> | |
</Prev> | |
) | |
}, | |
(props) => <React.Fragment {...props} />, | |
) | |
} | |
class FiberProvider extends React.Component { | |
componentDidMount() { | |
this.props.setFiber?.(this._reactInternals) | |
} | |
render() { | |
return this.props.children | |
} | |
} | |
const Canvas = React.memo((props) => { | |
const [fiber, setFiber] = React.useState(null) | |
const Bridge = useContextBridge(fiber) | |
render(<Bridge>{props.children}</Bridge>) | |
return <FiberProvider setFiber={setFiber} /> | |
}) | |
const Context1 = React.createContext(null) | |
const Context2 = React.createContext(null) | |
function Providers(props) { | |
const [value1, setValue1] = React.useState('value1') | |
const [value2, setValue2] = React.useState('value1') | |
const [mounted, setMounted] = React.useState(true) | |
React.useEffect( | |
() => | |
void requestAnimationFrame(() => { | |
requestAnimationFrame(() => setValue1('value1__new'), setValue2('value2__new')) | |
requestAnimationFrame(() => requestAnimationFrame(() => setMounted(false))) | |
}), | |
[], | |
) | |
return ( | |
<Context1.Provider value="invalid"> | |
<Context1.Provider value={value1}> | |
{mounted ? <Context2.Provider value={value2}>{props.children}</Context2.Provider> : props.children} | |
</Context1.Provider> | |
</Context1.Provider> | |
) | |
} | |
function Test() { | |
console.log(React.useContext(Context1), React.useContext(Context2)) | |
return null | |
} | |
ReactDOM.createRoot(document.getElementById('root')).render( | |
<React.StrictMode> | |
<Providers> | |
<Canvas> | |
<Test /> | |
</Canvas> | |
</Providers> | |
</React.StrictMode>, | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment