Skip to content

renderToStringAsync with NoHydration and nested resources + Suspense boundaries triggers resource collision #2546

@fongandrew

Description

@fongandrew

Describe the bug

Suppose you have the following Post component, which uses a resource to load a user:

async function loadPost(id) {
	await new Promise((resolve) => setTimeout(resolve, 10));
	return {
		id,
		userId: 'alice',
		content: 'Hello world',
	};
}

export function Post(props) {
	const [post] = createResource(
		() => props.id,
		(postId) => loadPost(postId),
	);

	return (
		<div>
			<Suspense>
				<User id={post()?.userId} />
			</Suspense>
			<span>Post: {post()?.content}</span>
		</div>
	);
}

The User component in turn loads data via a resource and renders that:

async function loadUser(id) {
	await new Promise((resolve) => setTimeout(resolve, 10));
	return {
		id,
		name: `User ${id}`,
	};
}

export function User(props) {
	const [user] = createResource(
		() => props.id,
		(id) => loadUser(id),
	);

	return <span>User: {user()?.name}</span>;
}

If you render with renderToStringAsync, this will wait for all the resources / Suspense boundaries to resolve. But if you render this under NoHydrate like so:

await renderToStringAsync(() => (
	<NoHydration>
		<Post id="post123" />
	</NoHydration>
));

This can result in the nested User resource returning the return value from the parent Post resource. So user() returns the same value as post() does.

This only happens with NoHydration. Removing it seems to resolve the issue.

Your Example Website or App

https://github.com/fongandrew/solidjs-resource-repro

Steps to Reproduce the Bug or Issue

  1. Nest components with createResource calls and Suspense boundaries
  2. Render with renderToStringAsync and <NoHydrate>

Expected behavior

I expect the accessor returned by each createResource to always return the value returned by the fetcher it was created with (or undefined if still loading). And I expect it to work with NoHydrate (the use case here is that we're just rendering a pure HTML page that will never be hydrated but still relies on a waterfall of async data loading).

Screenshots or Videos

No response

Platform

  • OS: macOS
  • Browser: Node
  • Version: 23.5.0

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions