react icon indicating copy to clipboard operation
react copied to clipboard

Is it possible to share contexts between renderers?

Open diegomura opened this issue 5 years ago • 18 comments

What is the current behavior?

Hey 👋 I maintain react-pdf. Thanks for your awesome work and making react-reconciler for us to use!

I've got many issues lately regarding context not working on my library and when doing tests I found out that context values aren't shared between renderers. This makes it impossible to share state such as themes, i18n, redux and more. As a bit of context, React-pdf is not a primary renderer, and as such, when used in the browser it runs on top of react-dom.

I found the isPrimaryRenderer reconciler option that's supposed to be used for "multiple renderers concurrently render using the same context objects" but still any access of the context inside react-pdf components get's just the initial value (even if the context was updated with other value). The same happens for react-art that also set isPrimaryRenderer=false.

Minimal demo

I prepared a quick demo using react-art so you can see how it currently works:

https://codesandbox.io/s/pedantic-hill-54kid?fontsize=14

What is the expected behavior?

Share contexts between renderers when using isPrimaryRenderer config. Is there a way of achieving this? Am I missing something?

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

React: 16.11.0 React-dom: 16.11.0

diegomura avatar Nov 05 '19 03:11 diegomura

Check out how react-art’s Surface component works. In particular, pass a class component’s “this” value to updateContainer:

https://github.com/facebook/react/blob/master/packages/react-art/src/ReactART.js

Hope that helps.

sophiebits avatar Nov 05 '19 16:11 sophiebits

Paging @drcmda and @lavrton , who might be interested in bridging contexts between renderers.

markerikson avatar Nov 06 '19 03:11 markerikson

@sophiebits is this possible with function components as well?

drcmda avatar Nov 06 '19 07:11 drcmda

@markerikson thanks for mentioning.

@sompylasar, unfortunately, it doesn't help. The minimal demo, posted by @diegomura, shows the issue with react-art.

Related issue: https://github.com/facebook/react/issues/13332

As far as I know, there are currently no ways to pass contexts automatically into other renderers. The workaround is to "bridge" the context manually:

<ThemeContext.Consumer>
      {value => (
        <Stage width={window.innerWidth} height={window.innerHeight}>
          <ThemeContext.Provider value={value}>
            <Layer>
              <ThemedRect />
            </Layer>
          </ThemeContext.Provider>
        </Stage>
      )}
</ThemeContext.Consumer>

lavrton avatar Nov 06 '19 14:11 lavrton

@lavrton I came up to the same conclusion, although in my quick test just adding the context provider inside my renderer elements was enough to bridge the state (without manually passing value from one consumer to the provider). But I might have to check that again.

@sophiebits thank you so much for you quick response, but as @lavrton said, this issue is also happening in react-art. Does that means this is a bug?

diegomura avatar Nov 06 '19 14:11 diegomura

Hi @lavrton , do you know why is not possible to pass context between renderers automatically? Sorry, it is still not clear to me why this doesn't work.

Coming from the same issue but with react-pixi:

Demo: https://codesandbox.io/s/react-pixi-context-example-eky26 Issue: https://github.com/inlet/react-pixi/issues/165

Thanks

RodrigoHamuy avatar Nov 13 '19 21:11 RodrigoHamuy

It only looks like the jsx you're giving to pixi via the stage component is in the same tree as the dom related jsx, which belongs to the react-dom reconciler, the one that holds the context provider. stage constructs a new reconciler, which is entirely isolated, and renders the children it received (the jsx inside stage). pixies reconciler has no idea where it is, it bears no relation to the surrounding reconciler and can't share its dynamics, like context, suspense, error boundaries and so on.

if react wanted to solve this we would probably need something like a first class react component, like a portal, which is returned by the react-reconcilers createContainer function. this will be rendered into the root reconciler as a regular element belonging to its tree. this way it would know it must pass its internals on. @gaearon described something like that once, but can't find it right now.

drcmda avatar Nov 13 '19 22:11 drcmda

This doesn't seem to be solved? I'd also really like to see this happen!

jquense avatar May 21 '20 15:05 jquense

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

stale[bot] avatar Aug 22 '20 19:08 stale[bot]

It's very much still affecting us

drcmda avatar Aug 22 '20 19:08 drcmda

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

stale[bot] avatar Dec 25 '20 14:12 stale[bot]

Sorry, bot. But many of us are still interested in the solution.

lavrton avatar Dec 25 '20 14:12 lavrton

HI, anyone have found good way to fix this ? tried out solutions suggested here but no luck (got errors from react it self after doing that)

hansagames avatar Mar 14 '22 07:03 hansagames

Still having the issue trying to use next-intl NextIntlClientProvider inside the react-pdf.

Error: Failed to call 'useTranslations' because the context from 'NextIntlClientProvider' was not found.

Minious avatar Jan 29 '24 10:01 Minious

BTW, with https://github.com/pmndrs/its-fine, the issue can be resolved. All custom renderers may use it to automatically bridge all the contexts. react-konva and react-three-fiber are already doing that.

lavrton avatar Jan 29 '24 13:01 lavrton

https://twitter.com/sebmarkbage/status/1765881794648269239 😂

but maybe this project was silly enough for react to see that this is needed. why have secondary renderers when they can't participate in primary matters.

drcmda avatar Mar 08 '24 09:03 drcmda

Sharing the context from @sebmarkbage on https://github.com/facebook/react/pull/28524:

Spent some time investigating. Regardless the versions would have to be in perfect lock-step but that turns out to be a requirement anyway so that's fine.

The proper solution that behaves like a Portal should forward the value during a concurrent render into the child. Meaning it should actually be part of the same render phase. That's not just an issue with Context but Props too. Ideally it shouldn't have to commit the outer root before forwarding the inner values.

Additionally, it's not just Context that is contextual. There's internal contexts and for example if you Suspend in the child renderer outside a Suspense boundary, it should affect the parent render. E.g. a startTransition in the parent should stall or .

That's the proper implementation. It's very difficult, and may require compromising on performance for everyone, but it's not impossible. Realistically, that amount of work, I don't see that getting prioritized any time soon. There's just way more pressing issues.

The smaller version of converting an outer render into a sync secondary render after commit, with no Suspense or Transition integration etc. is easier. That might be "good enough". However, there's the question of whether encouraging thinking of these is a single root given that new features won't work seamlessly anyway. So it might've been "good enough" before but not "good enough" in the future.

There's also a whole other approach of creating fake DOM nodes using the DOM renderer.

rickhanlonii avatar Mar 24 '24 15:03 rickhanlonii

I managed to achieve this by writing a shim for react context to use events, you can check it out here:

https://github.com/mathematikoi/eventually.git

still wip, but you can see for yourself

https://codesandbox.io/p/sandbox/context-events-qnqr4f?file=%2Fsrc%2F100%25-event-driven.tsx

mathematikoi avatar May 25 '24 02:05 mathematikoi