I have a wrapper component (Main.js) which wraps around all sites in my ReactJS project. I use multiple providers/contexts (MyProvider.js/YourProvider.js) which asynchronously load a value which is used in various child components. They are nested in my Main.js.
My problem is that the direct child components of Main.js (Child1.js) only receive undefined when accessing the contexts. Even useEffects do not detect the changes. The child components of the child components (Child2.js) also receive undefined, but are changed after.
Why is this the case?
Why does it only occur of the direct children and why does it work for the sub children?
Is this connected to the nesting of the providers?
Here a small example:
Main.js
import { MyProvider } from "../../contexts/MyProvider.js";
import { YourProvider } from "../../contexts/YourProvider.js";
import Header from "./Header.js";
import Footer from "./Footer.js";
export default function Main({children}) {
return (
<MyProvider>
<YourProvider>
<Header/>
<main>
<div>
{children}
</div>
</main>
<Footer/>
</YourProvider>
</MyProvider>
);
}
MyProvider.js/YourProvider.js
import { createContext, useState, useEffect } from 'react';
import { getMyValue } from "../../lib.js";
export const MyContext = createContext();
export function MyProvider({children}) {
const [myValue, setMyValue] = useState(undefined);
useEffect(() => {
getMyValue().then(result => {
setMyValue(result);
});
}, []);
return (
<MyContext.Provider value={myValue}>
{children}
</MyContext.Provider>
);
}
Child1.js
import { useContext, useEffect } from "react";
import { MyContext } from "../../contexts/MyProvider.js";
import { YourContext } from "../../contexts/YourProvider.js";
import Main from "./Main.js";
import Child2 from "./Child2.js";
export default function Child1({children}) {
const myValue = useContext(MyContext);
const yourValue = useContext(YourContext);
useEffect(() => {
console.log(myValue); // always undefined
console.log(yourValue); // always undefined
}, [myValue, yourValue]);
return (
<Main>
<p>{myValue}</p>
<p>{yourValue}</p>
<Child2/>
</Main>
);
}
Child2.js
import { useContext, useEffect } from "react";
import { MyContext } from "../../contexts/MyProvider.js";
import { YourContext } from "../../contexts/YourProvider.js";
export default function Child2({children}) {
const myValue = useContext(MyContext);
const yourValue = useContext(YourContext);
useEffect(() => {
console.log(myValue); // initially undefined, then the real value
console.log(yourValue); // initially undefined, then the real value
}, [myValue, yourValue]);
return (
<div>
<p>{myValue}</p>
<p>{yourValue}</p>
</div>
);
}
You are accessing MyContext
and YourContext
above Main
in Child1
. Context consumers must be children of their providers docs.
It works in Child2
because you are passing it as a child to Main
and Main
's children are defined as children of MyProvider
and YourProvider
.
What you need to do is move your providers above your consumers for example:
function App() {
return (
<Main>
<Child1/>
</Main>
);
}
export default function Child1({children}) {
const myValue = useContext(MyContext);
const yourValue = useContext(YourContext);
useEffect(() => {
console.log(myValue); // always undefined
console.log(yourValue); // always undefined
}, [myValue, yourValue]);
return (
<>
<p>{myValue}</p>
<p>{yourValue}</p>
<Child2/>
</>
);
}