context

Hi all, I have a question about context scoping.
import { render } from "solid-js/web";
import { useContext, createContext } from "solid-js";

const FooContext = createContext({ context: "not found" });

const FooHOC = (C: any) => (props: any) => {
return (
<FooContext.Provider value={props.name}>
<C {...props} />
</FooContext.Provider>
)
}

const C = () => {
console.log("C", useContext(FooContext));
const b1 = <B name={"B1"}/>;
const b2 = () => <B name={"B2"}/>;

return (
<A name={"A"}>
{b1}
{b2}
</A>
)
}

const B = (props: any) => {
console.log("B", props.name, useContext(FooContext));
return <div/>
};

const A = FooHOC((props: any) => {
console.log("A", useContext(FooContext))
return (
<div>
{props.children}
</div>
);
})

render(() => <C name={"C"} />, document.getElementById("app")!);`
import { render } from "solid-js/web";
import { useContext, createContext } from "solid-js";

const FooContext = createContext({ context: "not found" });

const FooHOC = (C: any) => (props: any) => {
return (
<FooContext.Provider value={props.name}>
<C {...props} />
</FooContext.Provider>
)
}

const C = () => {
console.log("C", useContext(FooContext));
const b1 = <B name={"B1"}/>;
const b2 = () => <B name={"B2"}/>;

return (
<A name={"A"}>
{b1}
{b2}
</A>
)
}

const B = (props: any) => {
console.log("B", props.name, useContext(FooContext));
return <div/>
};

const A = FooHOC((props: any) => {
console.log("A", useContext(FooContext))
return (
<div>
{props.children}
</div>
);
})

render(() => <C name={"C"} />, document.getElementById("app")!);`
/// logs emitted

C {context: 'not found'}
B B1 {context: 'not found'}
A A
B B2 A
/// logs emitted

C {context: 'not found'}
B B1 {context: 'not found'}
A A
B B2 A
In the example above, I have three main components and a HOC which wraps a component in a context. C renders A which contains 2 Bs as children. A also has a provider wrapped around it. Within all three components, I try to retrieve a context called FooContext which is provided by the aforementioned HOC. As expected I was not able to retrieve the context in C but I was initially surprised that I am not able to retrieve the context in the first B but I am able to in the second B. I assume that this is because it lazily initialized so it is put in the "expected" place in the owner tree. First I just wanted to check if this is expected behaviour. Secondly is there anyway to catch these kinds of subtle issues in the future e.g. in a lint rule?
3 Replies
peerreynders
peerreynders4w ago
I am not able to retrieve the context in the first B but I am able to in the second B
console.log("C", useContext(FooContext));
// JSX.Element
const b1 = <B name={"B1"}/>;
// () => JSX.Element
const b2 = () => <B name={"B2"}/>;
console.log("C", useContext(FooContext));
// JSX.Element
const b1 = <B name={"B1"}/>;
// () => JSX.Element
const b2 = () => <B name={"B2"}/>;
With b1 you are forcing JSX evaluation at setup time; so it's in exactly the same position as the console.log(). b2 is a function, so its evaluation is deferred until it is inside the returned JSX where the provider is active.
mangojassi
mangojassiOP4w ago
Yep makes sense. This seems to me like a really subtle difference with big implications. I can imagine myself and others forcing the evaluation at setup time but still thinking that they will be able to retrieve the context because they are still under A in the component tree. Do you think it would be reasonable to use a lint rule to disable these eager evaluations of components during setup to avoid this?
peerreynders
peerreynders4w ago
There was a whole discussion about this some time ago: https://discord.com/channels/722131463138705510/1238643925988937820 The thing is, sometimes you need early evaluation that is why children exists.

Did you find this page helpful?