honojs / hono

Web framework built on Web Standards
https://hono.dev
MIT License
17.09k stars 475 forks source link

attachReactJsxNode convenience feature to help injecting React components in the "faked/compatibility" react JSX #2757

Open timurxyz opened 1 month ago

timurxyz commented 1 month ago

What is the feature you are proposing?

#2508

Feature/Rationale

hono/jsx/dom in many cases now allows for interoperation with React UI libraries / components (guests) without actually running / mounting a real React renderer or explicitly wrapping user code in react-renderer middleware. However additional code/method sugar around in-place rendering of such guest React components might help code logic / DX.

Example of usage

We could take a React React component from shadcn or Tamagui or similar like: https://ui.shadcn.com/docs/components/tooltip and wrap it into a hono/honox (island) custom component like so (the styling issues are omitted here):

import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,} 
from '@/components/ui/tooltip'
import { attachReactJsxNode } from 'hono/jsx/dom'

export default function TtDemo (/* props/children in case of a button/etc */) {

  const tt = 
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger>Hover</TooltipTrigger>
        <TooltipContent>
          <p>Add to library</p>
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>

  return attachReactJsxNode(tt)  // children are handled in the JSX above
}

// On the higher level then: <><TtDemo /></> OR <MyTamaguiButton children attribs /> ...

(Also works with a simple Tamagui button as of 240520)

Reference implementation code of the idea

import { useEffect } from 'hono/jsx'
import { render } from 'hono/jsx/dom'
// import { v4 as uuidv4 } from 'uuid'
// import { nanoid } from 'nanoid/non-secure';

export function attachReactJsxNode (
  jsxNode: JSX.Element  // shall come from island or an other "browser-time" component (?) *
) {
  const mountPointId = _randomId()   // uuidv4() / nanoid()

  useEffect(() => {
    render(jsxNode, document.getElementById(mountPointId)!)
  }, [])

  return <span id={mountPointId} style={{display:'content'}} />
}

// if no random id lib used, then:
function _randomId () { return Math.random().toString(36).replace(/^0\./, '_') }

Note: Children are handled in the ingress JSX

The above sample implementation code works. The actual implementation might better utilize the hono/useEffect internals or other dom lifecycle related internals.

*Please extend with the SSR/SSG aspects.

usualoma commented 1 month ago

Hi @timurxyz

Thanks for creating the issue! Certainly this approach may make up for the incompleteness of hono's jsx. But hopefully we can get it to work without these workarounds.

https://ui.shadcn.com/docs/components/tooltip

Regarding this, just yesterday I created a PR that fixes jsx/dom and should work. This PR also adds useInsertionEffect. Would be glad to have you try to get it working on the PR branch if you can.

timurxyz commented 1 month ago

Regarding this, just yesterday I created a PR that fixes jsx/dom and should work. This PR also adds useInsertionEffect. Would be glad to have you try to get it working on the PR branch if you can.

Hi @usualoma , thank you!

And I tried your fix:

  1. shadcn. It functionally works, though it might be a styling issue in my env that it shows like: image

  2. The actual useInsertionEffect request came from my experimenting with Tamagui to be ran by the compatibility layer so I tried that with the 4.3.8. hono.tgz. Added a Tamagui test into your hono-react-compat-demo. Please check out the results: https://gitlab.com/6-1-6/xyz/usualoma-hono-react-compat-demo

(The above is not related to this 2757, but chose to answer here. And will return to the actual attachReactJsxNode "workaround" topic a bit later soon, have some thoughts to share with you, please dont close yet. Ty!)

usualoma commented 1 month ago

@timurxyz Thanks for confirming and commenting! I understand the situation.

shadcn. it functionally works, though it might be a styling issue in my env that it shows like:.

Sorry, my demo only focused on the result of DOM changes and omitted the application of styles.

I have created a separate issue for the lack of version and client APIs, which I would like to discuss here.

https://github.com/honojs/hono/issues/2792

yusukebe commented 1 month ago

Hi @timurxyz

Can we close this issue?

timurxyz commented 1 month ago

Hi, your patience in awaiting my answer is much appreciated!)

I suggest to consider that if we had a build-in wrapper like the above 'attachReactJsxNode' that could create a first-class pattern for incorporating user components or components of design-system libraries provided/existing as React components. The 'attachReactJsxNode'-like extension to the React emulation or even a higher level wrapper is the last mile for implementing such middleware (?) between hono code and those component sets. Which helps the DX with hono/honox.

The analogy could be:

 components: {
    ReactButton: applyPureReactInVue(ReactButton)
  },

approach allowed by 'veaury'.

In React itself there's no such thing obviously, but a complete emulation of the React API might need that as a complete one in terms of usage emulation. (But again, I might miss something already resolved in this regards.)