ReactJS with nested context providers: useContext always returns undefined

huangapple go评论45阅读模式
英文:

ReactJS with nested context providers: useContext always returns undefined

问题

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>
    );
}
英文:

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 &quot;../../contexts/MyProvider.js&quot;;
import { YourProvider } from &quot;../../contexts/YourProvider.js&quot;;
import Header from &quot;./Header.js&quot;;
import Footer from &quot;./Footer.js&quot;;

export default function Main({children}) {
    return (
        &lt;MyProvider&gt;
            &lt;YourProvider&gt;
	            &lt;Header/&gt;
	
	            &lt;main&gt;
		            &lt;div&gt;
			            {children}
		            &lt;/div&gt;
	            &lt;/main&gt;

	            &lt;Footer/&gt;
            &lt;/YourProvider&gt;
        &lt;/MyProvider&gt;
    );
}

MyProvider.js/YourProvider.js

import { createContext, useState, useEffect } from &#39;react&#39;;
import { getMyValue } from &quot;../../lib.js&quot;;

export const MyContext = createContext();

export function MyProvider({children}) {
	const [myValue, setMyValue] = useState(undefined);

	useEffect(() =&gt; {
		getMyValue().then(result =&gt; {
			setMyValue(result);
		});
	}, []);

	return (
		&lt;MyContext.Provider value={myValue}&gt;
			{children}
		&lt;/MyContext.Provider&gt;
	);
}

Child1.js

import { useContext, useEffect } from &quot;react&quot;;
import { MyContext } from &quot;../../contexts/MyProvider.js&quot;;
import { YourContext } from &quot;../../contexts/YourProvider.js&quot;;
import Main from &quot;./Main.js&quot;;
import Child2 from &quot;./Child2.js&quot;;

export default function Child1({children}) {
    const myValue = useContext(MyContext);
    const yourValue = useContext(YourContext);

    useEffect(() =&gt; {
        console.log(myValue); // always undefined
        console.log(yourValue); // always undefined
	}, [myValue, yourValue]);

    return (
        &lt;Main&gt;
	        &lt;p&gt;{myValue}&lt;/p&gt;
            &lt;p&gt;{yourValue}&lt;/p&gt;
            &lt;Child2/&gt;
        &lt;/Main&gt;
    );
}

Child2.js

import { useContext, useEffect } from &quot;react&quot;;
import { MyContext } from &quot;../../contexts/MyProvider.js&quot;;
import { YourContext } from &quot;../../contexts/YourProvider.js&quot;;

export default function Child2({children}) {
    const myValue = useContext(MyContext);
    const yourValue = useContext(YourContext);

    useEffect(() =&gt; {
        console.log(myValue); // initially undefined, then the real value
        console.log(yourValue); // initially undefined, then the real value
	}, [myValue, yourValue]);

    return (
        &lt;div&gt;
	        &lt;p&gt;{myValue}&lt;/p&gt;
            &lt;p&gt;{yourValue}&lt;/p&gt;
        &lt;/div&gt;
    );
}

答案1

得分: 1

你正在访问 Child1Main 上方的 MyContextYourContext。上下文消费者必须是其提供者的子元素文档

Child2 中可行,因为您将其作为 Main 的子元素传递,并且 Main 的子元素被定义为 MyProviderYourProvider 的子元素。

您需要做的是将提供者移到消费者上方,例如:

function App() {
    return (
        <MyProvider>
            <YourProvider>
                <Main>
                    <Child1/>    
                </Main>
            </YourProvider>
        </MyProvider>
    );
}
export default function Child1({children}) {
    const myValue = useContext(MyContext);
    const yourValue = useContext(YourContext);

    useEffect(() => {
        console.log(myValue); // 始终为undefined
        console.log(yourValue); // 始终为undefined
    }, [myValue, yourValue]);

    return (
        <>
            <p>{myValue}</p>
            <p>{yourValue}</p>
            <Child2/>
        </>
    );
}
英文:

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 (
        &lt;Main&gt;
            &lt;Child1/&gt;    
        &lt;/Main&gt;
    );
}
export default function Child1({children}) {
    const myValue = useContext(MyContext);
    const yourValue = useContext(YourContext);

    useEffect(() =&gt; {
        console.log(myValue); // always undefined
        console.log(yourValue); // always undefined
    }, [myValue, yourValue]);

    return (
        &lt;&gt;
            &lt;p&gt;{myValue}&lt;/p&gt;
            &lt;p&gt;{yourValue}&lt;/p&gt;
            &lt;Child2/&gt;
        &lt;/&gt;
    );
}

</details>



huangapple
  • 本文由 发表于 2023年5月25日 22:51:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76333619.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定