React.useMemo与React.useState差异探究:不变常量变量的两种实现对比及useRef使用困惑
Great question! Let's break down the key differences between these two approaches, especially since you're aware useRef is the intended tool but prefer a more declarative syntax without .current.
First, let's clarify a critical detail in your provided code:
Your Option B uses
useMemowithout a dependency array. This means the callback() => new Meshwill run every single time the component re-renders, creating a newMeshinstance on each render. That's almost certainly not what you want for an "unchanging constant." So I'll cover two scenarios for Option B: as-written, and with an empty dependency array (useMemo(() => new Mesh, [])) which aligns with your goal of one-time initialization.
1. Core Behavior & Instance Persistence
Option A: useState(() => new Mesh)
- The initialization callback (
() => new Mesh) runs only once: on the component's first render. React stores the resultingMeshinstance as state, and reuses it for all subsequent renders (even if the component re-renders multiple times). - In React Strict Mode (development environment), the callback runs twice, but React discards the first instance and keeps the second—so you still end up with a single persistent instance after the initial render cycle.
Option B (as-written: no dependency array)
- The
useMemocallback runs on every render, creating a newMeshinstance each time. This completely defeats the purpose of an "unchanging constant"—you'll get a new instance every time your component re-renders (e.g., when props or state change).
Option B (with empty dependency array: useMemo(() => new Mesh, []))
- The callback runs only once (first render, plus twice in Strict Mode like useState), and the
Meshinstance is cached for all subsequent renders. In terms of instance persistence, this behaves almost identically to Option A.
2. Semantic Intention & Code Clarity
This is where the biggest practical difference lies:
- Option A:
useStateis designed for managing mutable state (values you expect to update with a setter function). By using it to store an unchanging constant, you're misusing its intended purpose. Other developers reading your code will wonder: "Why is this a state variable if it never changes? Should I be updating it somewhere?" It creates unnecessary confusion. - Option B (with empty dependencies):
useMemois intended for caching expensive computation results. While creating aMeshinstance might not be "expensive," usinguseMemoat least signals that you want to avoid recreating the value on every render. This is more semantically appropriate thanuseState, but still not the perfect fit for a persistent constant that doesn't depend on any inputs. - useRef (your alternative): It's explicitly designed for persisting values across renders without triggering re-renders, which is exactly your use case. The
.currentsyntax might feel clunky, but it's a clear signal to other developers that this is a persistent reference, not state or a cached computation.
3. Edge Cases & Cleanup
- Neither
useStatenoruseMemoprovides built-in cleanup for theMeshinstance. If yourMeshrequires cleanup (e.g., releasing resources when the component unmounts), you'll need to pair either approach with auseEffecthook:
WithuseEffect(() => { return () => { // Clean up Mesh instance here Mesh.destroy(); }; }, [Mesh]);useRef, this would look similar but useMesh.currentinstead.
A Better Middle Ground (If You Hate .current)
Since you find useRef's syntax unappealing, you can wrap it in a custom Hook to get a declarative, .current-free experience:
function useConstant(initializer) { const ref = useRef(); // Initialize only once if (!ref.current) { ref.current = initializer(); } return ref.current; } // Usage in your component function MyComponent() { const Mesh = useConstant(() => new Mesh); // Use Mesh directly, no .current needed! return "...someRender"; }
This custom Hook gives you the persistence of useRef with the clean, declarative syntax you want—plus it's semantically clear to other developers that this is an unchanging constant.
内容的提问来源于stack exchange,提问作者pery mimon

