pmndrs / react-three-next

React Three Fiber, Threejs, Nextjs starter
https://react-three-next.vercel.app/
MIT License
2.52k stars 342 forks source link

Integrating with a @react-three/drei's ScrollControls, connecting state fails #102

Closed X-Equals-Zero closed 1 year ago

X-Equals-Zero commented 2 years ago

I like the way HTML and WebGL scrolling and animations can be synced and manipulated easily through the ScrollControls in NPM package '@react-three/drei' .

Basically, you put ScrollControls inside of a <Canvas>, and then any HTML components go inside a <Scroll html> within the <ScrollControls>, as such:

<Canvas> 
    <ScrollControls>
        <Scroll html>
            <div> Hello! </div>
        </Scroll>
        <Scroll>
            <mesh></mesh>
        </Scroll>
    </ScrollControls>
</Canvas>

WebGL meshes go alongside in their own <Scroll> component. Then all the useful scrolling properties like offset can be accessed through drei's hook, useScroll.

The problem I am facing is given this starter, the HTML part of the app goes inside the <Dom> element, while Three components go in the Component.r3f prop. Therefore, I had to put the <Dom> element inside of the <LCanvas>, rather than being separate:

function App({ Component, pageProps = { title: 'index' } }) {
  return (
    <>
      <LCanvas>
        <Scroll html>
          <Dom>
            <Component {...pageProps} />
          </Dom>
        </Scroll>
        {Component?.r3f && Component.r3f(pageProps)}
      </LCanvas>
    </>
  )
}

Since I need all HTML to be inside of the <Scroll html> tags for ScrollControls to work, and since the <Scroll> component MUST be placed within the canvas, I cannot have it like the original, which optimally would look like this:

function App({ Component, pageProps = { title: 'index' } }) {
  return (
    <>
        <Scroll html>
          <Dom>
            <Component {...pageProps} />
          </Dom>
        </Scroll>
      <LCanvas>

        {Component?.r3f && Component.r3f(pageProps)}
      </LCanvas>
    </>
  )
}

A problem I have hit: onCreated={(state) => state.events.connect(dom.current)} within the <Canvas> says that 'dom' is null, even though if I print to the console the const dom = useStore((state) => state.dom), it returns the correct reference (at first it prints 'null' but later it shows the correct ref..?), which is to the <Dom> element's ref.

Since <Dom> is a child of <LCanvas> now, does that mean I do not need the onCreated? Does this mean that canvas will re-render the HTML as well every time I take a route and I lose all the optimizations of this template? I cannot see an obvious solution to this problem.