meteor-blaze-react-component icon indicating copy to clipboard operation
meteor-blaze-react-component copied to clipboard

React-Blaze doesn't transfer Provider context for Consumer down the chain

Open lcampanis opened this issue 6 years ago • 5 comments

Hi there,

Using the new React Provider/Consumer context API in React >=16.3, it seems that the react-blaze component doesn't transfer the values down the chain, hence rendering undefined values for the Consumer inside a Blaze component.

Happy to provide further info if the module is maintained?

Thanks!

lcampanis avatar May 27 '18 15:05 lcampanis

Hi @lcampanis

Must admit, haven't touched this in ages, but as the package is still the official recommendation from the guide, I'd like to get this working.

Yes, please provide more details. Especially an example of in what situations a Blaze component would need the Context. Or did you mean something like React -> Blaze -> React again?

Gadi

gadicc avatar May 28 '18 14:05 gadicc

Hi @gadicc

Thanks for jumping in on this. The problem is in React -> Blaze -> React

A core parent React template -> renders a core layout Blaze component -> which renders React subcomponents. In this case references cannot be forwarded down or child refs be seen by the parent.

This issue is most probably an existing issue. As the parent cannot see any refs from the child, past the Blaze react render.

However, back to our issue - using the new context API, one would expect the context variables to be passed down the chain and be used by the child anywhere in the tree.

Take this for example:

Parent template

<UiManagerContext.Provider value={{ ...this.state }}>
  <Blaze template="my-child-template" />
</UiManagerContext.Provider>

Child template One level down (or anywhere after Blaze template is rendered)

render() {
  return (
    <UiManagerContext.Consumer>
      {({ parentState }) => (
        <ChildComponent childProp={parentState} />
      )}
    </UiManagerContext.Consumer>
  );
}

In this case parentState should be the state of the parent, but it's undefined.

Let me know if you need more info, but if you replicate a classic Provider/Consumer scenario with the new React 16.4 context API you'll get it.

Many thanks!

lcampanis avatar Jun 03 '18 20:06 lcampanis

Did you figure this out or any workaround @lcampanis ?

gunnartorfis avatar Sep 04 '19 16:09 gunnartorfis

We're running into this as well.

Our use case is a stop gap solution while porting over our core Blaze templates to React. I'd like to basically do what @lcampanis refers to:

  1. Have top-level React wrapper. Provide context here.
  2. Render our legacy Blaze layout inside of that.
  3. Have leaf React components downstream. Hook into context from 1).

When logging context in 3), it's just the default value from 1).

I have no idea if this is possible in this package, however :O I bet there are many chains in between 1) and 3) which are using multiple ReactDOM.render which are breaking the context chain.

brookback avatar Feb 04 '20 10:02 brookback

Uh wow sorry I really apologise that I never replied since my last post, I have actually spent a fair amount of time looking at this issue and regret not writing my findings.

@brookback you're exactly right. <Blaze> is rendered directly into a div inside the existing React render / context. When I say "directly", I mean, Blaze is doing the rendering and writing to the DOM directly inside that div.

The problem of course is when you render React inside of Blaze, presumably using {{> React}}. This indeed is a new ReactDOM.render instance, completely isolated, with no connection to the existing React tree.

I don't believe there's any good way to connect these separate React instances. It might be possible to do in a very hacky way on the React side, and then involving collaboration between both this project and react-template-helper / {{> React}}. But I believe such a hack would be very brittle and not worth persuing.

I think the sensible thing to do here is to use to simply use again any providers you need on the next level of the tree. I think this is clear but e.g.

// Level 1: react root ("A")
const root = () => (
  <SomeContext.Provider value={}>
    <Blaze template="reactParent" />
  </SomeContextProvider>
<!-- Level 2: blaze template ("B") that will be rendered in react root context ("A") -->
<template name="reactParent">
  {{>React component=ChildComponent}}
</template>
// Level 3: react component ("C") that will be rendered into blaze component ("B")
// Use the Provider again to bring context to this new part of the tree
const ChildComponent = () => (
  <SomeContext.Provider value={}>
    <NextLevelOfReactStuffWhichNeedsContext />
  </SomeContextProvider>
);

gadicc avatar Feb 04 '20 11:02 gadicc