-
-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[question] Recommended strategies to pass context #31
Comments
Hey @eelkevdbos. Awesome question btw. The context concept is really useful to have changing-state in multiple branches of your component tree, without having to drill However, this is not present in this library, as each component is immutable and only returns a string. Although it would be awesome to be able to define per-render "global" variables that could be used deep inside components, we cannot do this without changing the return type to something else than a string/Promise or keep asynchronous safe renders. I did a lot of experiments and even looked up similar implementations like the new beth-stack. All of them either have a different return type or, like in the beth stack, are not asynchronous safe. The asynchronous problem happens in cases like this example: async function AsyncComponent({ userId }) {
const username = await dbQueryThatTakesOneSecondOrMore(userId);
return <div safe>{username}</div>
} In a web server like backend, which will be most of usecases, having a http route that renders the above async component, would work normally for all cases. If we change it to use a "context-like" approach: async function AsyncComponent() {
const requestContext = useContext(Request);
const username = await dbQueryThatTakesOneSecondOrMore(requestContext.userId);
return <div safe>{username}</div>
} However, without changing the string-like return type, it would need some sort of per-request variable that is defined at the beginning of the request and cleared out after it renders successfully. The problem happens when a second request is received without having returned the previous one. (This example of 1+ seconds is easily to reproduce). NodeJS is asynchronous, and the event loop would jump a non blocking task until it completes. Which would conflict with the global variable usage and break implementations completely by returning wrong states in async components when two requests are being rendered at the same time. Which is something really important but may not be obvious. A solution I found was, like in the Suspense implementation, fallback to a I also tried implementations with the // rid was a native prop, the same way as children is defined for every component.
function Component({rid}) {
const contextA = useContext(MyContextA, rid);
const contextB = useContext(MyContextB, rid);
return <div>...</div>;
}
const html = renderWithContext(rid => (
<Layout rid={rid}>
<ComponentA rid={rid} />
<Componentb rid={rid} />
<ComponentC rid={rid} />
<ComponentD rid={rid} />
</Layout>
), { stateProperty }) But as you saw, we would still need to prop-drill With all of the above in mind. I'm not sure we will be able to do any context-like implementation. But remember, Anyways, happy to discuss further possibilities :) |
Thank you for the thorough explanation! I think I understand the issue a little more. Coming from a python background, I am familiar with the concept of a Context Local (https://docs.python.org/3/library/contextvars.html), a seemingly "global" variable but with a thread or event loop local scope. After a bit of searching for an analogy, I found the following: https://nodejs.org/api/async_context.html It seems to me that we could use this (if ported to Bun) to have that "global" async-safe variable. |
Somehow I totally missed async_context module... I'll take a deeper look at it. |
This should be a totally doable feature, however I do not have time to spend on it as I'm using my spare time to build https://kita.js.org. Any help is appreciated, does someone wanna work on it? |
Hey all! 👋🏻
I am not sure if this should be handled by I have tested to use the ALS of AdonisJS and it played nicely with router.get('test', async () => {
return <Home title={'Hello'} />
}) import { HttpContext } from '@adonisjs/core/http'
export default function Home({ title }: { title: string }) {
const ctx = HttpContext.getOrFail()
return (
<div>
<h1>
{title} - {ctx.request.url()}
</h1>
</div>
)
} |
Awesome! I'm mainly developing kitajs/html for kita.js.org, which uses fastify under the hood, so a more specific implementation towards https://github.com/fastify/fastify-request-context should be done here, however we must also document usage with other libraries. |
Keep a note that using ALS is quite expensive and will decrease the performance of your application. If we can avoid using ALS, it would be better. |
The only way to avoid ALS is by not avoiding prop-drilling, which is the topic of this issue. Buut, i'm 100% with prop drilling, as its more verbose but hides any kind of magic may happeng. Also, it is expensive at the point of frameworks having to revert their ALS adoption. |
As our only way to support concurrent renders and avoid prop drilling is to use ASL, but its slower, I'll close this issue until newer ways are released. Also, hooks are a "mentality" of react, this library, unless attached to another framework, should be thought as a template engine, not a frontend framework. |
Just putting this here, because if implemented, it would be a decent performance improvement to the async local storage: nodejs/node#48528 |
awesome! |
FYI, we wrote a blog post on how to use Kita with AdonisJS and someone made a package to have a better DX! |
Hey @RomainLanz, awesome post!!! I would greatly appreciate it if you could also provide guidance on preventing XSS. This can be achieved either by incorporating the xss-scan script from @kitajs/ts-html-plugin into the package.json test script, or by configuring IDE IntelliSense. While using kita/html correctly has no XSS vulnerability, overlooking certain steps can be quite risky. I'm kindly requesting everyone who showcases kitajs/html to include this information, as it's not a common practice for other libraries. https://github.com/kitajs/ts-html-plugin?tab=readme-ov-file#running-as-cli |
Yes, I will add those tomorrow! |
I have updated the article; if you have any other feedback about missing things, I would happily add them too! 👍🏻 |
Seeing as a library like this will be used as a (lightweight) templating engine, I wonder what strategies could be used to inject context into a template to prevent property drilling.
Example use cases:
I've searched for some packages that provide a similar feature for rendering JSX to HTML without React in the mix:
I would love to hear your thoughts on this @arthurfiorette
The text was updated successfully, but these errors were encountered: