Next.js Discord

Discord Forum

How to handle search, filters and product selection?

Unanswered
Tan posted this in #help-forum
Open in Discord
TanOP
Hello I am making a fabric viewer in 3D but I don't understand how to split the components and fetch data dynamically when I select a new fabric.

the structure of the page is like this:
-- fabrics page
---- Viewer3D (yellow)
---- ProductDetails (blue)
---- ProductCatalog (green)
------ SearchInput
------ ProductFilters

Here are the features I would like to implement:
- Search
- Filter
- SelectProduct and view product details, 3d fabric and related products.

I cannot reload the filters page because the 3D viewer should be always ON and it will take some time to load if I reload every time.

It's basic interactivity but I'm a bit confused how to break things properly and make them communicate between them correctly. Could you give me some tips and point me to the right direction?

1 Reply

If you're using @react-three/fiber you can set it up so that the canvas persists between route changes. This way, you can load in your r3f as server components to keep your routing. I also use a package called tunnel-rat to help with this.

In my root layout (my app is full screen, so may need to be modified for yours)
'use client'

import { useRef } from 'react'
import dynamic from 'next/dynamic'
const Scene = dynamic(() => import('./Scene'), { ssr: false })

const Layout = ({ children }) => {
    const ref = useRef()

    return (
        <div
            ref={ref}
            style={{
                position: 'relative',
                width: ' 100%',
                height: '100%',
                overflow: 'auto',
                touchAction: 'auto',
            }}
        >
            {children}
            <Scene
                style={{
                    position: 'fixed',
                    top: 0,
                    left: 0,
                    width: '100vw',
                    height: '100vh',
                    pointerEvents: 'none',
                }}
                eventSource={ref}
                eventPrefix='client'
            />
        </div>
    )
}

export { Layout }


// Scene.jsx
"use client";

// This is used for the root layout. You probably want ./View.jsx instead
import { Canvas } from "@react-three/fiber";
import { Preload } from "@react-three/drei";
import { r3f } from "./global/rat";

export default function Scene({ ...props }) {
  // Everything defined in here will persist between route changes, only children are swapped
  return (
    <Canvas {...props}>
      {/* @ts-ignore */}
      <r3f.Out />
      <Preload all />
    </Canvas>
  );
}


// ./global/rat.js
import tunnel from 'tunnel-rat'

export const r3f = tunnel()


This allows you to create a "view" anywhere on the page inline with your other HTML.
// ./View.jsx
"use client";

import { forwardRef, Suspense, useImperativeHandle, useRef } from "react";
import { PerspectiveCamera, View as ViewImpl } from "@react-three/drei";
import { Three } from "./Three";

export const Common = ({ color }) => (
  <Suspense fallback={null}>
    {color && <color attach="background" args={[color]} />}
    <ambientLight intensity={0.5} />
    <pointLight position={[20, 30, 10]} intensity={1} />
    <pointLight position={[-10, -10, -10]} color="blue" />
    <PerspectiveCamera makeDefault fov={40} position={[0, 0, 6]} />
  </Suspense>
);

const View = forwardRef(({ children, ...props }, ref) => {
  const localRef = useRef(null);
  useImperativeHandle(ref, () => localRef.current);

  return (
    <>
      <div ref={localRef} {...props} />
      <Three>
        <ViewImpl track={localRef}>{children}</ViewImpl>
      </Three>
    </>
  );
});
View.displayName = "View";

export { View };