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

[🐞] "Manually" rendered component have no way to access context providers #7181

Open
jwickers opened this issue Dec 20, 2024 · 0 comments
Labels
STATUS-1: needs triage New issue which needs to be triaged TYPE: bug Something isn't working

Comments

@jwickers
Copy link
Contributor

Which component is affected?

Qwik Runtime

Describe the bug

We are using the render method from @builder.io/qwik to dynamically generate components on a page based on a CMS returned HTML.
Unfortunately any attempt to call useContext(...) in those fail with an error that the context is not provided, this also affect the Qwik City contexts needed for useNavigation / useLocation.

For example:

	useVisibleTask$(({ track }) => {
		// contentRef is a ref to div where we inserted the dynamic content
		const el = track(() => contentRef.value)
		if (!el) return
		el?.querySelectorAll('my-component')?.forEach((el) => {
			// render the components and pass all the attributes from the HTML element to the component
			const attrs = Array.from(el.attributes).reduce(
				(acc, attr) => ({ ...acc, [attr.name]: attr.value }),
				{}
			) as MyComponentProps
			render(el, <MyComponent {...attrs} />)
		})
	})

Would fail if MyComponent is using useNavigate().

After quite a long time trying to figure out how the context is resolved in Qwik I managed to get this to work by addind an option to the render method:

/** @public */
export declare interface RenderOptions {
    serverData?: Record<string, any>;
    contextEl?: Element;
}

then in the render method

  const rCtx = createRenderContext(doc, containerState);
  // CUSTOM: allow passing a context element to the render function
  //  so that the rendered components have access to context providers
  //  defined in the parent component
  if (opts.contextEl) {
    const ctxEl = getElement(opts.contextEl);
    const ctxElState = _getContainerState(ctxEl);
    const ctx = findParentCtx(ctxEl, ctxElState);
    rCtx.$cmpCtx$ = ctx;
  }
  // CUSTOM END

Finally changing the render call to be:

	useVisibleTask$(({ track }) => {
		// contentRef is a ref to div where we inserted the dynamic content
		const el = track(() => contentRef.value)
		if (!el) return
   
		// ADDED: get the Element for *this* qwik component since we want the inserted
		//  component to inherit the contexts this one has access to then pass it as opts to render
		const contextEl = el.parentElement?.parentElement || undefined
		el?.querySelectorAll('my-component')?.forEach((el) => {
			// render the components and pass all the attributes from the HTML element to the component
			const attrs = Array.from(el.attributes).reduce(
				(acc, attr) => ({ ...acc, [attr.name]: attr.value }),
				{}
			) as MyComponentProps
			render(el, <MyComponent {...attrs} />, { contextEl })
		})
	})

I am not sure if this is supposed to work, or work another way or maybe this hack is something that could useful to others.
Note: this was on Qwik 1.x

Reproduction

https://stackblitz.com/edit/github-1r3nusmb?file=src%2Froutes%2Findex.tsx

Steps to reproduce

No response

System Info

System:
    OS: Linux 6.8 Ubuntu 24.04.1 LTS 24.04.1 LTS (Noble Numbat)
    CPU: (32) x64 Intel(R) Core(TM) i9-14900KF
    Memory: 44.03 GB / 62.49 GB
    Container: Yes
    Shell: 5.2.21 - /bin/bash
  Binaries:
    Node: 21.6.2 - ~/.nvm/versions/node/v21.6.2/bin/node
    npm: 10.2.4 - ~/.nvm/versions/node/v21.6.2/bin/npm
    pnpm: 9.14.4 - ~/.local/share/pnpm/pnpm
  Browsers:
    Brave Browser: 131.1.73.101
    Chrome: 131.0.6778.139
  npmPackages:
    @builder.io/partytown: 0.10.2 => 0.10.2
    @builder.io/qwik: ^1.11.0 => 1.11.0
    @builder.io/qwik-city: ^1.11.0 => 1.11.0
    typescript: 5.6.2 => 5.6.2
    vite: 5.x => 5.1.4

Additional Information

No response

@jwickers jwickers added STATUS-1: needs triage New issue which needs to be triaged TYPE: bug Something isn't working labels Dec 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
STATUS-1: needs triage New issue which needs to be triaged TYPE: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant