-
My goal: toggle the opacity of a three.js mesh without re-rendering the component. I can do this in Zustand using subscribe. So far I haven't worked out if it's possible in Jotai. I've set up a sandbox to show what I mean here: The scene contains two meshes and two toggle button that control the opacity of each mesh. One uses Jotai, the other uses Zustand: function DodecahedronJotai({ ...props }) {
console.log("render Jotai")
const [transparentJotai] = useAtom(transparentAtom)
const material = useRef()
useEffect(() => {
material.current.opacity = transparentJotai ? 0.5 : 1
}, [transparentJotai])
return (
<mesh {...props}>
<dodecahedronBufferGeometry attach="geometry" />
<meshStandardMaterial ref={material} attach="material" transparent={true} roughness={0.75} emissive="#404057" />
</mesh>
)
}
function DodecahedronZustand({ ...props }) {
console.log("render Zustand")
const material = useRef()
useEffect(() => {
useStore.subscribe(
(transparent) => {
console.log(material)
material.current.opacity = transparent ? 0.5 : 1
},
(state) => state.transparent
)
}, [material])
return (
<mesh {...props}>
<dodecahedronBufferGeometry attach="geometry" />
<meshStandardMaterial ref={material} attach="material" transparent={true} roughness={0.75} emissive="#404057" />
</mesh>
)
} The two toggle buttons: const ToggleButtonJotai = () => {
const [transparent, setTransparent] = useAtom(transparentAtom)
return (
<button
onClick={() => {
setTransparent(!transparent)
}}>
Toggle Transparency (Jotai)
</button>
)
}
const ToggleButtonZustand = () => {
const { transparent, setTransparent } = useStore((state) => state)
return (
<button
onClick={() => {
setTransparent(!transparent)
}}>
Toggle Transparency (Zustand)
</button>
)
} Whenever I click Is it possible to transiently update the opacity of a mesh using Jotai? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 10 replies
-
Short answer is no. It's like useState, so updating ref requires useEffect like you do. function DodecahedronJotai({ ...props }) {
console.log("render Jotai")
const material = useRef()
return (
<>
<mesh {...props}>
<dodecahedronBufferGeometry attach="geometry" />
<meshStandardMaterial ref={material} attach="material" transparent={true} roughness={0.75} emissive="#404057" />
</mesh>
<SyncTransparent material={material} />
</>
)
}
function SyncTransparent({ material }) {
const [transparentJotai] = useAtom(transparentAtom)
useEffect(() => {
material.current.opacity = transparentJotai ? 0.5 : 1
}, [transparentJotai])
return null
} Another hack would be to store such a ref in an atom, but it's tricky and there's no guarantee that it works in the future version. |
Beta Was this translation helpful? Give feedback.
-
With the release of v1.6.0, Jotai can now directly implement transient updates, which is very exciting! import ReactDOM from "react-dom"
import React, { useEffect, useRef } from "react"
import { Canvas } from "@react-three/fiber"
import { OrbitControls } from "@react-three/drei"
import { useAtom, unstable_createStore, Provider } from "jotai"
import { transparentAtom, useStore } from "./state"
import "./styles.css"
const jotaiStore = unstable_createStore()
function DodecahedronJotai({ ...props }) {
console.log("render Jotai")
const material = useRef()
const opacity = jotaiStore.get(transparentAtom) ? 0.5 : 1
useEffect(() => {
jotaiStore.sub(transparentAtom, () => {
console.log("transparentAtom changed")
const transparent = jotaiStore.get(transparentAtom) // this can be the same value as the previous one
material.current.opacity = transparent ? 0.5 : 1
})
})
return (
<mesh {...props}>
<dodecahedronBufferGeometry attach="geometry" />
<meshStandardMaterial ref={material} opacity={opacity} attach="material" transparent={true} roughness={0.75} emissive="#404057" />
</mesh>
)
}
function DodecahedronZustand({ ...props }) {
console.log("render Zustand")
const material = useRef()
useEffect(() => {
useStore.subscribe(
(transparent) => {
console.log(material)
material.current.opacity = transparent ? 0.5 : 1
},
(state) => state.transparent
)
}, [material])
return (
<mesh {...props}>
<dodecahedronBufferGeometry attach="geometry" />
<meshStandardMaterial ref={material} attach="material" transparent={true} roughness={0.75} emissive="#404057" />
</mesh>
)
}
const ToggleButtonJotai = () => {
const [transparent, setTransparent] = useAtom(transparentAtom)
return (
<button
onClick={() => {
setTransparent(!transparent)
}}>
Toggle Transparency (Jotai)
</button>
)
}
const ToggleButtonZustand = () => {
const { transparent, setTransparent } = useStore((state) => state)
return (
<button
onClick={() => {
setTransparent(!transparent)
}}>
Toggle Transparency (Zustand)
</button>
)
}
ReactDOM.render(
<Provider unstable_createStore={() => jotaiStore}>
<ToggleButtonJotai />
<ToggleButtonZustand />
<Canvas style={{ background: "#0e0e0f" }} camera={{ position: [0, 0, 7.5] }}>
<pointLight color="indianred" />
<pointLight position={[10, 10, -10]} color="orange" />
<pointLight position={[-10, -10, 10]} color="lightblue" />
<OrbitControls />
<DodecahedronJotai position={[-2, 0, 0]} />
<DodecahedronZustand position={[2, 0, 0]} />
</Canvas>
</Provider>,
document.getElementById("root")
) |
Beta Was this translation helpful? Give feedback.
Short answer is no.
It's like useState, so updating ref requires useEffect like you do.
One thing you might try is to update the ref in a child component.