React context from library not updating in consumer - reactjs

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):
Here's Provider showing a value:
Here's AppWithAuthentication's consumer showing a value:
And here's the consumer from inside my application that doesn't have a 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?

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.

Related

React class component api call in componentDidMount() called twice

New to React here,
I'm building a class component with Typescript. I need to fetch data from the server when component displays. componentDidMount() seems to be the right place to do that; but it is always called twice.
I found its because of the <React.StrictMode> in my index.tsx (and should'nt be called twice in prod mode).
But in the meantime I feel pretty annoying that my api is called twice everytime while I'm developing my app.
Any way I can avoid that?
Thanks

Make library chunks share the same react context with webpack

I have two projects, one is a react app, the other is a library used by that react app.
The library is bundled with webpack and output three chunks, one called contexts, one called hooks, and one called components
In contexts can be found "myContext" react context (myLib/src/contexts/myContext.js)
In hooks can be found "useMyContext" react hook (myLib/src/hooks/useMyContext.js)
And in components can be found "MyProvider" component (myLib/src/components/MyProvider.jsx)
MyProvider component renders the myContext.Provider, giving it as value the output of the useMyContext.
Now in my react app, I render a component "MyComponent" wrapped with the "MyProvider" imported from my library.
"MyComponent" consumes the myContext imported from my library, and renders its value.
I would expect "MyComponent" to renders the value returned by the "useMyContext" hook, as it is implemented into "MyProvider"
However, "MyComponent" renders the initial value defined in myContext.
Note that if I export myContext and MyProvider in a same chunk, then it's working as expected, but I can't go for that solution in my real use case.
This makes me think that the myContext exported from the contexts chunk is not the same as the myContext used into MyProvider.
I made React a peerDependency in my package.json, to be sure the library relies on the React version from the app, and I made it an externals in Webpack config for the same reason.
What I tried so far was to use the Webpack feature "dependOn", in order to share react across the several chunks. But when I try that, the app raises error "__webpack_require__ is not a function" at runtime.
You can find the repository here https://github.com/c0ll1d3r/ReactContextLibraryIssue
Am I on the right track using dependOn ? If yes, would you know why I got that error ? Else what would be correct solution ?
Thank you for your help !
I found a way to make it works, actually as I mentioned, exporting everything from the same chunk was working (but not affordable in my project).
From that observation I made the conclusion the issue was that somehow two instances of "myContext" were created when imported from (or used in) two different chunks.
MyProvider was importing the context like this : import myContext from '../../myContext
And my app was importing the context like this : import { myContext } from 'myLib/contexts'
So I isolated my context in its own library, in order to be able to import it in a consistent way no matter what
Now, MyProvider and my app are importing this context the same way : import { myContext } from 'mycontextlib'
Making sure they are working on the same instance, and the value is well propagated

Read and write to Redux store from multiple pages of a Gatsby App

My app is like this:
Gatsby project
index.tsx - Entry point of app - I call
<Provider store={store}>
<HomePage />
</Provider>
HomePage.js uses mapStateToProps and uses connect(mapStateToProps)(HomePage)
Inside my HomePage, of course I use multiple React components. And I can make use of the store, read/write of anything that happens on that HomePage.
On my Homepage, I have links to ActivityPage. Once I click on this page and land there - problem starts.
ActivityPage makes use of multiple sub-components to achieve tasks. How do I access read/write from ActivityPage?
I am unable to useDispatch to write or even read from the store. I can see the store on my ReactDev tools, but on trying to read/write I get 11 errors, mostly along the lines of I need to wrap Components with <Provider>
I read different solutions on Stack Overflow which suggested that I need to have mapStateToProps and use connect on whichever Component I need access the store.
This is not working, as I am specifically looking to get access to the store from a 'new page'. Do I need to go and make another store for my child pages? Help please.
:
Your component must be inside of a redux Provider component in order to use the redux hooks useSelector and useDispatch or the connect HOC.
In a typical React app you would place the Provider in your App component which is a parent of everything. However Gatsby uses a different setup where each page is totally independent, so there is no shared parent where we can place the Provider.
What Gatsby does have is an API for overriding customizing behaviors by defining functions in configuration files. There are a bunch of files that you can place in the root of your app, in the same folder as package.json and outside of src. The two that we will use here are gatsby-browser.js, which controls the client-side, and gatsby-ssr.js, which controls the creation of static HTML pages through server-side rendering. Both of these files support a function called wrapRootElement.
We use the wrapRootElement function to place our Redux provider as a wrapper around every page. Since we are using the same function in two files, the official "using-redux" example defines that function in a separate file and imports it into both of the configurations. wrap-with-provider.js is not a special file name, it's just a holder for the function.
A wrapRootElement function receives the element as a prop which is similar to the children prop. Our function creates a store instance and returns a Provider with that store which has the element as its child. We are creating the store in this function, so if your current redux file is exporting a created store as a constant, you'll want to export a callable function that creates the store instead. You can see their example here.
wrap-with-provider.js
import React from "react"
import { Provider } from "react-redux"
import createStore from "./src/state/createStore"
// eslint-disable-next-line react/display-name,react/prop-types
export default ({ element }) => {
// Instantiating store in `wrapRootElement` handler ensures:
// - there is fresh store for each SSR page
// - it will be called only once in browser, when React mounts
const store = createStore()
return <Provider store={store}>{element}</Provider>
}
Again, this file does not manipulate Gatsby behavior on its own. We need to export its value and assign it to a special variable in a configuration file to do that. Here are those files:
gatsby-browser.js
import wrapWithProvider from "./wrap-with-provider"
export const wrapRootElement = wrapWithProvider
gatsby-ssr.js
import wrapWithProvider from "./wrap-with-provider"
export const wrapRootElement = wrapWithProvider
The content is the same in both. All we are doing is importing the function that we created in wrap-with-provider.js and assigning it to the special named variable wrapRootElement which controls behavior.
The answer posted already and the Freecodecamp link posted in the comment were both really helpful resources, however for my specific use case I found looking at the Gatsby repository on redux the most helpful": https://github.com/gatsbyjs/gatsby/tree/master/examples/using-redux
Also, I ended up realizing that sessionStorage was more appropriate for my use case as I was trying to store data beyond a page refresh and I wasn't using the other features Redux offered. You can read more about localStorage here: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
and the differences between Redux and local storage here: Why redux instead of session storage
Also if you're using Redux I highly reccomend the redux-devtools extension for debugging you can install the package here: https://www.npmjs.com/package/#redux-devtools/extension and the browser extension here:
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en

Every react.js package works normally with next.js after the javascript enters the application?

I'm new to using Next.js to create websites with react.js. I am trying to put the module "react-insta-stories" (https://github.com/mohitk05/react-insta-stories). Only he accuses that the document is not defined. I know you've had similar questions here, but I wanted to ask you something different, more general ... Because there are several awesome modules that I wanted to use in this project and the next.js seems to make it difficult. Some modules just that only speak with the "browser" . The question is ... can I use any module even from react.js after javascript instantiates my application with no problems with next.js? And does anyone know how I could put this react-insta-stories in my project? Because I tried some solutions, but I think they are beyond my knowledge.
By default next.js performs server side rendering. The quick and dirty way of getting react components that require the document or window to work is to call them as a dynamic component in next.
If you can a better way that allows for server side rendering of your component is to run this code in a useEffect hook or similar, as this will be sent to the browser to run.
For example
import React, { useEffect, useRef } from 'react'
function ComponentWithEffect(): JSX.Element {
const divRef = useRef<HTMLDivElement>(null)
useEffect(() => {
divRef.current?.width = window.width
divRef.current?.height = window.height
}, [])
return(
<div ref={divRef}/>
)
}
But this is not always possible, because with external libraries you might not have access to the underlying methods. There are heaps of examples in the repo as well.

Slowly implementing redux to an existing React.js project

I want to migrate my project from plain react to react redux, I am not new to React but new to Redux.
I have a fairly big web app written in React, dozens of React.js files.
most of them containing state's + passing variables between them.
including allot of Post/Get requests functions, implemented into at least half of my files.
I want to slowly move from plain react to react redux.
I wanted to ask if anyone have some article or can give an insight about migrating existing react project to react-redux.
I dont want to stop development for the sole purpose of the change but instead to slowly adapt to it.
is it possible ? is there a tool to help me do it ?
I saw some redux examples where entire render of app.js was surrounded by <Provider> </Provider>, does that mean every component inside <Provider> bracelet can not have it's own state ?
can I simply keep my old components as they are and put new ones into <Provider> </Provider> ?
Thanks in advance!
I saw some redux examples where entire render of app.js was surrounded
by , does that mean every component inside
bracelet can not have it's own state ?
The way react-redux works is by exposing a store prop, provided by the Provider. In order to consume it, or extract data from it you must wrap your component by it (not directly necessarily, but one of the parents must be a provider). In general in most apps you would simply wrap the entire application with a Provider, because for the most part, if you've chosen to introduce redux into your application, it is probably because your entire app needs some store.
Using redux does not mean that components can't have state. There is a big difference between global state - something that should be accessible to every component in your app (if the component chooses to "consume" it), and state that is private to a component - e.g. form changes before being sent to the server.
can I simply keep my old components as they are and put new ones into ?
Well, yes. But also - no. As I said earlier, you should probably start from the top and slowly drill down. Wrap your app with a Provider, and start moving your application state from the top-most component to the store. Once you get more comfortable with redux in general, you should start replacing the props you pass down the component tree with props from the state by connecting your inner components.
This way you can do it one component at a time without breaking existing logic.

Resources