React safe way to mutate memoized resources

Author: krispyaCreated Sep 5, 2026Updated Sep 5, 2026

It was always a violation React's rules to mutate values returned by useMemo and useState but we did it anyways.

javascript
// Before: mutating an object stored in React state
const material = useMemo(() => new THREE.MeshStandardMaterial(), []);

useFrame(() => {
  material.opacity = 0.5;
});

return <primitive object={material} attach="material" />;

But now the React compiler actually errors this out. In React's model, a ref is the actual mutable concept.

javascript
const material = useMemo(() => new THREE.MeshStandardMaterial(), []);
const materialRef = useRef<THREE.MeshStandardMaterial>(null);

useFrame(() => {
  if (materialRef.current) {
    materialRef.current.opacity = 0.5;
  }
});

return (
  <primitive
    object={material}
    ref={materialRef}
    attach="material"
  />
);

Here is a more thorough discussion: https://github.com/reactwg/react-compiler/discussions/14

Following their lead, a React safe way of creating mutable resources would look something like this where there is an explicit setter. We can use setters to mutate, but we cannot use assignment!

javascript
const material = useResource(() => {
  const value = new MeshStandardMaterial({ transparent: true });
  return { 
    value, 
    cleanup: () => value.dispose() 
  };
});

useFrame(() => {
  material.set(m => m.opacity = 0.5)
});

return <primitive object={material.value} attach="material" />;

Source: pmndrs/react-three-fiber