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

<A> not properly triggering nested Suspense #352

Open
hamoto opened this issue Jan 11, 2024 · 3 comments
Open

<A> not properly triggering nested Suspense #352

hamoto opened this issue Jan 11, 2024 · 3 comments

Comments

@hamoto
Copy link

hamoto commented Jan 11, 2024

Describe the bug

When using A components for navigation to pages which rely on params or location to render, it is not properly triggering nested Suspense fallbacks. It instead waits until the entire page is rendered off screen and only then updates the page content as well as the browser URL path and sets the activeClass accordingly. I understand that the component has built in transitions and that this may be intended behavior, but this causes applications with slow-loading API requests to feel unresponsive.

I've written a very simple example to recreate the issue:

// @refresh reload
import "./app.css";
import {A, Router, Route, cache} from "@solidjs/router";
import {lazy, Suspense} from "solid-js";

export const resource = cache((foo) => {
    return new Promise(resolve => setTimeout(() => resolve({ foo }), 2000))
}, "test")

export default function App() {
    const loadResource = ({params, location}) => {
        void resource(params.foo)
    }

    return (
        <Router root={(props) => {
            return (
                <>
                    <div className={"flex gap-5"}>
                        <A href={"/test1"} activeClass={"underline"}>Test 1</A>
                        <A href={"/test2"} activeClass={"underline"}>Test 2</A>
                        <A href={"/test3"} activeClass={"underline"}>Test 3</A>
                    </div>
                    <Suspense fallback={"Loading..."}>{props.children}</Suspense>
                </>
            )
        }}>
            <Route path={"/*foo"} component={lazy(() => import("~/Test.jsx"))} load={loadResource}></Route>
        </Router>
    );
}

/*
//Contents of Test.jsx

import {createAsync} from "@solidjs/router";
import {resource} from "~/app.jsx";
import {Suspense} from "solid-js";

export default function Test(props) {
    const testResource = createAsync(() => resource(props.params.foo))

    return (
        <Suspense fallback={"Loading 2..."}><pre>{JSON.stringify(testResource())}</pre></Suspense>
    )
}
 */

Steps to Reproduce the Bug or Issue

Start a new solid-start project, paste the above code into app.jsx, create a new file called Test.jsx in /src and paste the commented out "Contents of Test.jsx" there, run the server and click the Test links

Expected behavior

When the link is clicked, the page URL should update immediately, the clicked link should be assigned its active class ("underline" in this example) and the "Loading 2..." Suspense fallback should be triggered, then 2 seconds later it should be replaced with something like {"foo":"test3"}

Screenshots or Videos

unresponsive_links

Platform

  • OS: Windows
  • Browser: Chrome
  • Version: 0.10.8

Additional context

No response

@hamoto
Copy link
Author

hamoto commented Jan 11, 2024

Changing the Suspense in Test.jsx to a Show with useIsRouting() corrects the issue:

import {createAsync, useIsRouting} from "@solidjs/router";
import {resource} from "~/app.jsx";
import {Show} from "solid-js";

export default function Test(props) {
    const isRouting = useIsRouting()
    const testResource = createAsync(() => resource(props.params.foo))

    return (
        <Show when={!isRouting()}><pre>{JSON.stringify(testResource())}</pre></Show>
    )
}

this is a viable workaround for the time being.

@mshlz
Copy link

mshlz commented Jan 21, 2024

I am having the same problem, the suspense fallback is only being activated when I refresh the page itself. None of <A>, <a> or useNavigate triggers it

I made another example to simulate these other cases...

Dependencies:

"@solidjs/router": "0.10.9"
"solid-js": "1.8.11"

"typescript": "5.3.3",
"vite": "5.0.12",
"vite-plugin-solid": "2.8.2"

// test1.tsx, test2.tsx, test3.tsx
export default function TestN () {
    return <h1>TestN</h1>
}
// index.tsx
/* @refresh reload */
import { A, Route, Router, useNavigate } from "@solidjs/router"
import { Suspense, lazy } from "solid-js"
import { render } from "solid-js/web"

const delay = async (ms) => new Promise(resolve => setTimeout(resolve,ms))

// lazy pages
const Test1 = lazy(async () => {
  await delay(1000)
  return import("./test1")
})

const Test2 = lazy(async () => {
  await delay(1000)
  return import("./test2")
})

const Test3 = lazy(async () => {
  await delay(1000)
  return import("./test3")
})

// home page
const Home = () => <h1>Home</h1>

// wrapper with suspense
const AppWrapper = (props) => {
  const navigate = useNavigate()
  return (
    <>
      <nav style={{ display: "flex", gap: "1rem"}}>
        <A href="/test1">Test1</A>
        <a href="/test2">Test2</a>
        <p onclick={() => navigate("/test3")}>Test3</p>
      </nav>
      <hr />
      <Suspense fallback={<h1>Loading...</h1>}>
        {props.children}
      </Suspense>
    </>
  )
}

const dispose = render(() => (
  <Router root={AppWrapper}>
    <Route path="/" component={Home} />
    <Route path="/test1" component={Test1} />
    <Route path="/test2" component={Test2} />
    <Route path="/test3" component={Test3} />
  </Router>
), document.getElementById("root"))

if (import.meta.hot) {
  import.meta.hot.dispose(dispose)
}

EDIT:

Following @hamoto suggestion, I was able to achieve the behavior that I need too (show the Suspense fallback while the lazy import is not completed). In my case I wrapped the {props.children} with the <Show when={!isRouting()}>

@DontBreakAlex
Copy link

Also encountering this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants