Skip to content
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

@astrojs/solid-js renders solid-js components twice #8368

Closed
1 task
btakita opened this issue Sep 1, 2023 · 7 comments
Closed
1 task

@astrojs/solid-js renders solid-js components twice #8368

btakita opened this issue Sep 1, 2023 · 7 comments
Labels
- P2: nice to have Not breaking anything but nice to have (priority)

Comments

@btakita
Copy link

btakita commented Sep 1, 2023

Astro Info

Astro                    v3.0.6
Node                     v18.17.0
System                   Linux (x64)
Package Manager          unknown
Output                   server
Adapter                  @astrojs/node
Integrations             @astrojs/solid-js

If this issue only occurs in one browser, which browser is a problem?

All

Describe the Bug

The astro request renders the solid-js components 2 times.

if (await r.ssr.check.call({ result }, Component, props, children)) {

({ html, attrs } = await renderer.ssr.renderToStaticMarkup.call(

What's the expected result?

The astro request should render the solid-js components 1 time. Perhaps the result of r.ssr.check.call could be cached to be used again in enderer.ssr.renderToStaticMarkup.call

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-za4fie?file=package.json,src%2Fcomponents%2FC.tsx,src%2Fpages%2Findex.astro&on=stackblitz

Participation

  • I am willing to submit a pull request for this issue.
@github-actions github-actions bot added the needs triage Issue needs to be triaged label Sep 1, 2023
@natemoo-re
Copy link
Member

Unfortunately this is because we can't detect if a given function is a Solid component until we run it. Our React and Preact integrations do the same thing.

If you'd like to submit a PR to add a caching strategy, feel free!

@natemoo-re natemoo-re added the - P2: nice to have Not breaking anything but nice to have (priority) label Sep 21, 2023
@github-actions github-actions bot removed the needs triage Issue needs to be triaged label Sep 21, 2023
@matthewp
Copy link
Contributor

Closing since there is no claim that this breaks something, only that is non-ideal. Happy to receive a PR improving this though.

@forloopy
Copy link

forloopy commented Feb 11, 2024

@matthewp
@natemoo-re
@btakita

I have a relatively straightforward solution for this, working in my projects.

Using Astro 3.4.2, I have updated the following file:

astro/dist/runtime/server/render/component.js

Replacing these lines [97 - 112]:

if (!renderer) {
    let error;
    for (const r of renderers) {
        try {
	    if (await r.ssr.check.call({ result }, Component, props, children)) {
                renderer = r;
	       break;
	    }
        } catch (e) {
            error ??= e;
	}
    }
    if (!renderer && error) {
        throw error;
    }
}

...with the following:

if (!renderer) {
    let error;
    try {
        // Single renderer
        if (Object.keys(renderers).length === 1) {
            renderer = renderers[0];
        }
        // Multiple renderers
        else {
            // Look for an override variable in the jsx/tsx component function, using the format, e.g. const renderer = 'solid-js'
            var regex = /(const|let|var)+[\s{1}]+(renderer)+[\s{1}]+[={1}]+[\s{1}]+(['"]{1})+(?<name>react|preact|solid-js|vue)+(['"]{1})/gm
            var matches = Array.from(Component.toString().matchAll(regex));
            if (!matches.length) {
                // No override, use first available renderer
                renderer = renderers[0];
            }
            else {
                // Use override renderer, if available
                for (const r of renderers) {
                    if (matches[0].groups.name === r.name) {
                        renderer = r;
                        break;
                    }
                }
            }
        }
    } catch (e) {
        error ??= e;
    }
    if (!renderer && error) {
        throw error;
    }
}

This approach removes the need for having to call a component twice, as it's relatively easy to determine which framework should be used, without having to use r.ssr.check.call(...).

It would seem logical that if only one framework has been added to the Astro project, e.g. Solid JS, then this should be the framework being used by default. Alternatively, if multiple frameworks have been added to the project, there needs to be an easy way to override the default framework.

This solution uses a simple variable in the component, to set a preferred renderer, e.g. const renderer = 'react';

Here's an example function with an override variable:

function MyComponent( props: any ) {
    const renderer = 'react';    
    return (
        <div>
            <span>This component will be rendered using React</span>        
        </div>
    );
}

If you think this is worth a PR, please let me know and I'll happily go ahead.

@PeterDraex
Copy link
Contributor

@forloopy I like that there's no check for single renderer

@btakita
Copy link
Author

btakita commented Feb 26, 2024

Closing since there is no claim that this breaks something, only that is non-ideal. Happy to receive a PR improving this though.

@matthewp It actually made creating inline citations & rendering the citations as a footnote way more difficult than it should have been. It's one of the reasons why I no longer use Astro & wrote my own MPA stack. I think this should be taken seriously. It may not affect many devs with simpler requirements, but it's good to have system that works as expected in case someone wants to extend it. Meta note: having to jump through extra hoops to report an architectural issue that is difficult to reproduce is not developer-friendly. It's a trade off & I can see why you require a repro case...but making devs to more work to report an issue takes time & effort that could be spent on describing the issue in more detail + finding workarounds.

@forloopy Thank you for the solution. If I were still using Astro I would use it.

@Eworonuk
Copy link

Eworonuk commented Mar 6, 2024

@forloopy I would be happy if you made that PR! This has been a big annoyance for me in my project. The regex is a fine approach, or at minimum the check for a single renderer would probably cover a lot of use cases.

@elite174
Copy link

elite174 commented Oct 6, 2024

Hey guys, I made a PR for the case when there's only one renderer. I suppose that for multiple renderers it should be handled somehow differently (maybe), but at lease for one renderer we could do better.

#12131

@Eworonuk @natemoo-re @matthewp

Thank you @forloopy for your solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
- P2: nice to have Not breaking anything but nice to have (priority)
Projects
None yet
Development

No branches or pull requests

7 participants