-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support cross-renderer portals #13332
Comments
Thanks for working on this! This was a big pain point for us so we made a workaround. In case this is useful, here's an example of the Context barrier (for // 👎 Context cannot go through <Canvas>, so <Square> cannot read the rotation
ReactDOM.render(
<TickProvider>
<Canvas>
<Square />
</Canvas>
<Consumer>{value => value.toFixed(2)}</Consumer>
</TickProvider>,
document.getElementById('outside')
);
// 👎 Context is all inside <Canvas> so it cannot be passed/read from outside
ReactDOM.render(
<>
<Canvas>
<TickProvider>
<Square />
</TickProvider>
</Canvas>
No access to `rotation` here
</>,
document.getElementById('inside')
);
// 👍 Passes the Context from above, bridging React and react-three-fiber Context
// 👎 But this re-renders <Canvas> a lot, partially defeating the point of Context
// 👍 memo() Canvas and Square well enough and there's no problem here!
ReactDOM.render(
<TickProvider>
<Consumer>
{value => (
<Canvas>
<Provider value={value}>
<Square />
</Provider>
</Canvas>
)}
</Consumer>
<Consumer>{value => value.toFixed(2)}</Consumer>
</TickProvider>,
document.getElementById('bridge')
); ❤️❤️❤️ |
And just to bring it up once, this also applies to error boundaries and suspense. It would be really helpful if a reconciler could read out contextual information (context, errors, suspense) from its parent reconciler and apply it to itself - i think it shouldn't behave much different than a generic portal. |
Well, I hope this gains traction at some point as it's a huge pain point for me right now. |
Hi! Any plans on making it come true? |
I made something similar to this through a simple implementation that can be shared between renderers. import React from "react";
import ReactDOM from "react-dom";
import { Portal, createBridge } from "@devsisters/react-pixi";
import { CountContext } from "./countContext";
const $uiRoot = document.getElementById("ui-root");
const uiRoot = ReactDOM.createRoot($uiRoot);
const uiBridge = createBridge(uiRoot, {
sharedContext: [CountContext],
});
const UIPortal = ({ children }) => {
return <Portal bridge={uiBridge}>{children}</Portal>;
};
export default UIPortal; Demo: https://codesandbox.io/s/react-pixi-portal-lsgh1 User can specify the context object to share, and Portal uses React internal readContext() to pass these contexts to another renderer. |
I wanted to follow up to say that the solution I proposed above has been working very well for us for 1+ year. The main thing (as I edited in a comment there) is that you have to |
We rolled out a function SceneWrapper() {
// bridge any number of contexts
const ContextBridge = useContextBridge(ThemeContext, GreetingContext)
return (
<Canvas>
<ContextBridge>
<Scene />
</ContextBridge>
</Canvas>
)
}
function Scene() {
// we can now consume a context within the Canvas
const theme = React.useContext(ThemeContext)
const greeting = React.useContext(GreetingContext)
return (
//...
)
} It's been working out really well and the api is pretty friendly :) |
Hi, Interface is rather simple:
Components within TunnelEntrance will be send to TunnelExit. They will use the renderer of TunnelExit and therefore can consume its contexts.
Edit: There seem to be some race-conditions leading to unwanted side-effects. I.e. if tunnel entrance is used earlier than tunnel exit, components within entrance will not be able to consume context properly. This means, that the components are partly processed already before being supplied to the exit node. |
I would like to get back to the first idea of @gaearon . Currently we have solutions for portals within one renderer and context bridges to pass data to a child renderer. However, the idea of the above approach would allow bi-directional communication of renderers, maybe even while using them in parallel. I am currently facing a situation, where my 2d renderer needs to communicate with a 3d renderer. I could use the react tree approach and change states within a branch of the 2d renderer, throwing it into a context that is bridged down to the 3d renderer and update states over there. ( I cannot do it the other way around with bridges ) In this case I have to deal with data in three different placces. The 2d component, the context and the 3d component. If we would have a portal like above, we could just keep the logic within a single component. The use of zustand might not be preferable and it currently can lead to side effects in rare cases, that I do not fully understand yet. But I would hope, that there is a bit new research on this topic. Unfortunately I studied archtitecture and not software architecture, so there is a limit to my input :P |
we added our fix to react-three-fiber today https://twitter.com/0xca0a/status/1573064826339094528 it's using https://github.com/pmndrs/its-fine#fiberprovider (this would work in any react-renderer) if any of this could still land officially in react we would be relieved to say the least. not being able to access context across renderers is a major annoyance for our users, and even though forwarding context was always possible, there is a ton of libraries out there not willing to expose context. |
Currently
createPortal
only works within the current renderer.This means that if you want to embed one renderer into another (e.g.
react-art
inreact-dom
), your only option is to do an imperative render in a commit-time hook likecomponentDidMount
orcomponentDidUpdate
of the outer renderer's component. In fact that's exactly howreact-art
works today.With this approach, nested renderers like
react-art
can't read the context of the outer renderers (#12796). Similarly, we can't time-slice updates in inner renderers because we only update the inner container at the host renderer's commit time.At the time we originally discussed portals we wanted to make them work across renderers. So that you could do something like
But it's not super clear how this should work because renderers can bundle incompatible Fiber implementations. Whose implementation takes charge?
We'll want to figure something out eventually. For now I'm filing this for future reference.
The text was updated successfully, but these errors were encountered: