Hoping someone can help me out here. Im quite new to RecoilJS so if Im missing something obvious, please let me know.
I am trying to manage the state of 3D objects in a scene with RecoilJS Atoms. I have an atom for the last item the mouse hovered over and I want to show a debug panel with its info. For some reason the RecoilRoot provider doesn't seem to be accessible from within the ThreeJS canvas.
In Viewer (code below), I get an error warning me that This component must be used inside a <RecoilRoot> component
when I try to declare const [hoveredLED, setHoveredLEDAtom] = useRecoilState(hoveredLEDAtom);
(full trace below)
However, passing setHoveredLEDAtom
down from the parent (Viewer) works.
Declaring it within Debug also works, which is a sibling of Canvas sharing the same contexts
This is fine for now, but the whole point of moving to Recoil was to stop passing props up and down.
Am I missing something obvious or does the ThreeJS canvas somehow exist in a different scope.
Any pointers would be appreciated.
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>
);
}
Viewer
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;
Error
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.
From : https://recoiljs.org/docs/api-reference/core/useRecoilBridgeAcrossReactRoots/
So bridging the contexts is a simple as creating a bridge and nesting it within the ThreeJS Canvas