React context from library not updating in consumer
Asked Answered
L

1

5

I originally followed this project to add Firebase to a Gatsby React app. It involves making a Firebase context, wrapping a root layout with a provider, and then using a withFirebase HOC to wrap components with a Firebase consumer as needed. When I originally did it, it worked fine, but I wanted to move the code into a package I could reuse among my apps. Here's the HOC

export const withFirebase = (Component) => (props) => (
  <FirebaseContext.Consumer>
    {(firebase) => <Component {...props} firebase={firebase} />}
  </FirebaseContext.Consumer>
);

And each page starts with a Layout component that renders this:

<FirebaseContext.Provider value={this.state.firebase}>
  <AppWithAuthentication>
    {this.props.children}
  </AppWithAuthentication>
</FirebaseContext.Provider>

AppWithAuthentication itself uses the withFirebase HOC as it needs Firebase to get the AuthUser (which is then stored in a context and passed down through a provider), and it's able to do so just fine.

All the above happens in the package code itself, but when I imported my package into my other React project, trying to use withFirebase stops working as any components wrapped with it never receive the updated context. I confirm this by checking the Component tree in React Dev tools, the Firebase Provider gets the updated not-null value, and the consumer inside AppWithAuthentication gets it too. But the consumers inside my actual app don't update (and I have this same problem with the AuthUser context I made in the same library).

I even thought that perhaps somehow the parent was rendering with the updated consumer but the children weren't re-rendering, but after counting the renders and logging them it was clear the components from my app were rendering more times than AppWithAuthentication. To make it a bit clearer, here's my component tree (starting from the Layout component at page root):
Component Tree

Here's Provider showing a value:
Provider with value

Here's AppWithAuthentication's consumer showing a value:
Consumer with value

And here's the consumer from inside my application that doesn't have a value:
Consumer without value

I'm completely stuck here and would appreciate any insight.

EDIT: After more testing I found some more information but I'm still stuck. It would seem that when reloading my page, the Layout component renders 2 times, the Header and AppWithAuthentication components each renders 4 times, and the Login component renders only 1 time. Is this why the consumers aren't updating? (But then why does the Header component not get any updates when its updating as much as AppWithAuthentication?)

EDIT 2: After more research, I think this issue has something to do with webpack? I'm using Neutrino.js to make my component library, and I take the output of its build as the library. I found this question that seemed similar and tried implementing the fix like so in my .neutrinorc.js:

const reactComponents = require('@neutrinojs/react-components');

module.exports = {
  use: [reactComponents(),
  (neutrino) => {
     neutrino.config.output.library("upe-react-components");
     neutrino.config.output.libraryTarget("umd");
     neutrino.config.mode("development");
    }],
};

But it didn't fix the issue. Has anyone encountered issues with webpack breaking React context?

Lungwort answered 5/8, 2020 at 14:58 Comment(0)
P
6

I heard from a clever friend of mine that the problem is because React contexts are defined at the module-level, and so since you tried to have your contexts in separate entrypoints, things will not go so well.

I think you can try making an index.js file that re-imports and exports everything, and then things should work as it's all consolidated in one module.

Pender answered 5/8, 2020 at 17:9 Comment(1)
Yep that ended up fixing it for the time being. There's a post I found that describes it further: https://mcmap.net/q/586566/-react-leaflet-custom-component-context-not-being-passed I'll accept this answer for now but I'm hoping that I can find a different workaround as having a single entrypoint makes importing a lot messier.Lungwort

© 2022 - 2024 — McMap. All rights reserved.