在 ThreeJS 中无法访问 RecoilJS RecoilRoot Canvas

RecoilJS RecoilRoot not accessible within ThreeJS Canvas

希望有人能帮助我。 我对 RecoilJS 很陌生,所以如果我遗漏了一些明显的东西,请告诉我。

我正在尝试使用 RecoilJS Atoms 管理场景中 3D 对象的状态。 我有一个鼠标悬停在最后一项的原子,我想显示一个调试面板及其信息。 出于某种原因,似乎无法从 ThreeJS canvas.

中访问 RecoilRoot 提供程序

在查看器(下面的代码)中,当我尝试声明 const [hoveredLED, setHoveredLEDAtom] = useRecoilState(hoveredLEDAtom);(下面的完整跟踪)

时,我收到一条错误警告 This component must be used inside a <RecoilRoot> component

但是,从父级(查看器)向下传递 setHoveredLEDAtom 是可行的。

在 Debug 中声明它也可以,它是 Canvas 共享相同上下文的兄弟

目前这很好,但转向 Recoil 的全部意义在于停止上下传递道具。

我是否遗漏了一些明显的东西,或者 ThreeJS canvas 是否以某种方式存在于不同的范围内。

任何指点将不胜感激。

index.js


const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  // <React.StrictMode>
  <RecoilRoot>
    <App />
  </RecoilRoot>
  // </React.StrictMode>

App.js

function App() {
  return (
    <div className="App">
      <Viewer />
    </div>
  );
}

观看者

const LED = ({ led }) => {
  const [hovered, setHoevered] = useState(false);
  const [hoveredLED, setHoveredLEDAtom] = useRecoilState(hoveredLEDAtom);

  const handleHoverEnter = () => {
    setHoveredLEDAtom(led);
    setHoevered(true);
  };
  const handleHoverExit = () => {
    setHoevered(false);
  };

  return (
    <mesh
      onPointerOver={(event) => handleHoverEnter()}
      onPointerOut={(event) => handleHoverExit()}
    >
      <coneGeometry />
      <meshStandardMaterial
        color={hovered || led.brightness > 125 ? "hotpink" : "grey"}
      />
    </mesh>
  );
};
const Debug = () => {
  const [hoveredLED, setHoveredLEDAtom] = useRecoilState(hoveredLEDAtom);
  return (
    <>
      <div style={{ position: "absolute", left: "10px", top: "1rem" }}>
        member : {hoveredLED.member}
      </div>
    </>
  );
};
const Viewer = () => {
  const [model, setModel] = useRecoilState(modelAtom);
  const [hoveredLED, setHoveredLEDAtom] = useRecoilState(hoveredLEDAtom);

  useEffect(() => {
    console.log(hoveredLED);
  }, [hoveredLED]);

  return (
    <>
      <Debug />
      <Canvas camera={{ position: [5, 7, 5] }} style={{ height: "700px" }}>
        <Helpers />
        <OrbitControls />
        {model.map((led, index) => {
          const key = `led-${index}`;
          return (
            <LED key={key} led={led} />
          );
        })}
      </Canvas>
    </>
  );
};

export default Viewer;

错误

995 react-reconciler.development.js:9747 The above error occurred in the <LED> component:

    at LED (http://localhost:3000/static/js/bundle.js:205:5)
    at Suspense
    at ErrorBoundary (http://localhost:3000/static/js/bundle.js:1998:5)
    at Provider (http://localhost:3000/static/js/bundle.js:3860:5)

React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.
logCapturedError @ react-reconciler.development.js:9747
2 react-three-fiber.esm.js:141 Uncaught Error: This component must be used inside a <RecoilRoot> component.
    at err (recoil.js:16:1)
    at Object.notInAContext (recoil.js:4092:1)
    at updateRetainCount (recoil.js:3255:1)
    at useRetain_ACTUAL (recoil.js:4669:1)
    at useRetain (recoil.js:4627:1)
    at useRecoilValueLoadable (recoil.js:5234:1)
    at useRecoilValue (recoil.js:5258:1)
    at useRecoilState (recoil.js:5306:1)
    at LED (Viewer.js:76:1)
    at renderWithHooks (react-reconciler.development.js:7363:1)
react-dom.development.js:18525 The above error occurred in the <ForwardRef(Canvas)> component:

    at Canvas (http://localhost:3000/static/js/bundle.js:4179:5)
    at Viewer (http://localhost:3000/static/js/bundle.js:325:83)
    at div
    at App
    at RecoilRoot_INTERNAL (http://localhost:3000/static/js/bundle.js:91093:5)
    at RecoilRoot (http://localhost:3000/static/js/bundle.js:91259:5)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
logCapturedError @ react-dom.development.js:18525
react-dom.development.js:26740 Uncaught Error: This component must be used inside a <RecoilRoot> component.
    at err (recoil.js:16:1)
    at Object.notInAContext (recoil.js:4092:1)
    at updateRetainCount (recoil.js:3255:1)
    at useRetain_ACTUAL (recoil.js:4669:1)
    at useRetain (recoil.js:4627:1)
    at useRecoilValueLoadable (recoil.js:5234:1)
    at useRecoilValue (recoil.js:5258:1)
    at useRecoilState (recoil.js:5306:1)
    at LED (Viewer.js:76:1)
    at renderWithHooks (react-reconciler.development.js:7363:1)
three.module.js:26599 THREE.WebGLRenderer: Context Lost.

来自 : https://recoiljs.org/docs/api-reference/core/useRecoilBridgeAcrossReactRoots/

If a nested React root is created with ReactDOM.render(), or a nested custom renderer is used, React will not propagate context state to the child root. This hook is useful if you would like to "bridge" and share Recoil state with a nested React root. The hook returns a React component which you can use instead of in your nested React root to share the same consistent Recoil store state. As with any state sharing across React roots, changes may not be perfectly synchronized in all cases.

所以桥接上下文很简单,只需创建一个桥并将其嵌套在 ThreeJS 中 Canvas

const Viewer = () => {
  const RecoilBridge = useRecoilBridgeAcrossReactRoots_UNSTABLE();
  ...

  return (
    <>
      <Canvas>
        <RecoilBridge>
            ...
        </RecoilBridge>
      </Canvas>
    </>
  );
};

export default Viewer;