The "useRef: the value returned should be a valid value or reference" error occurs when React's useRef hook is used incorrectly, typically when trying to access or modify the ref.current property during rendering, passing invalid values to useRef, or using refs in ways that violate React's rendering rules.
This error message indicates that React's useRef hook is being used in a way that violates its intended purpose or React's rendering rules. The useRef hook creates a mutable reference object with a current property that persists across component renders without triggering re-renders. This error typically surfaces when developers attempt to read or write to ref.current during the rendering phase, pass invalid initial values to useRef, or use refs in contexts where they shouldn't be used. React enforces strict rules about when refs can be accessed because refs are designed to store mutable values that don't affect the visual output. Reading or writing refs during rendering can lead to unpredictable behavior since React may call component functions multiple times (especially in Strict Mode during development). The error serves as a guardrail to prevent common anti-patterns that could break React's rendering consistency or cause subtle bugs in your application.
Ensure you only read or write ref.current in event handlers, useEffect, or useLayoutEffect, not during rendering:
// ❌ WRONG - Accessing ref during render
function MyComponent() {
const inputRef = useRef(null);
// 🚩 Reading ref during render
console.log(inputRef.current);
// 🚩 Writing ref during render
inputRef.current = "some value";
return <input ref={inputRef} />;
}
// ✅ CORRECT - Access ref in event handler
function MyComponent() {
const inputRef = useRef(null);
function handleClick() {
// ✅ Reading ref in event handler
console.log(inputRef.current?.value);
// ✅ Writing ref in event handler
inputRef.current.focus();
}
return <input ref={inputRef} onClick={handleClick} />;
}
// ✅ CORRECT - Access ref in effect
function MyComponent() {
const inputRef = useRef(null);
useEffect(() => {
// ✅ Safe to access ref in effect
inputRef.current?.focus();
}, []);
return <input ref={inputRef} />;
}Refs are for side effects and should not affect what gets rendered.
Pass the actual value you want as the initial ref.current, not a function or expression that needs evaluation:
// ❌ WRONG - Passing a function instead of value
function MyComponent() {
// 🚩 Function will be called, but ref.current becomes the function
const ref = useRef(() => new ExpensiveObject());
// Now ref.current is a function, not an ExpensiveObject
ref.current(); // This calls the function
}
// ✅ CORRECT - Pass the value directly
function MyComponent() {
const ref = useRef(new ExpensiveObject());
// ref.current is now an ExpensiveObject
ref.current.doSomething();
}
// ✅ BETTER - Lazy initialization for expensive objects
function MyComponent() {
const ref = useRef(null);
if (ref.current === null) {
ref.current = new ExpensiveObject(); // Only created once
}
return <div>{/* ... */}</div>;
}For expensive objects, use lazy initialization to avoid recreating on every render.
When passing a ref to a custom component, you must use forwardRef to expose the underlying DOM element:
// ❌ WRONG - Custom component doesn't forward ref
function MyInput(props) {
return <input {...props} />;
}
function Parent() {
const inputRef = useRef(null);
// 🚩 Ref won't work - MyInput doesn't forward it
return <MyInput ref={inputRef} />;
}
// ✅ CORRECT - Using forwardRef
const MyInput = forwardRef(function MyInput(props, ref) {
return <input {...props} ref={ref} />;
});
function Parent() {
const inputRef = useRef(null);
// ✅ Ref works - forwarded to actual input
return <MyInput ref={inputRef} />;
}forwardRef passes the ref through to the underlying DOM element or component.
If changing a value should cause the component to re-render, use useState instead of useRef:
// ❌ WRONG - Using ref for state that should trigger re-render
function Counter() {
const countRef = useRef(0);
function increment() {
countRef.current += 1;
// 🚩 Component won't re-render!
}
return (
<div>
<button onClick={increment}>Increment</button>
<div>Count: {countRef.current}</div> {/* 🚩 Won't update! */}
</div>
);
}
// ✅ CORRECT - Using state for reactive values
function Counter() {
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1); // ✅ Triggers re-render
}
return (
<div>
<button onClick={increment}>Increment</button>
<div>Count: {count}</div> {/* ✅ Updates automatically */}
</div>
);
}Use refs for values that persist but don't affect rendering; use state for reactive values.
Don't access ref.current in conditions or expressions that affect what gets rendered:
// ❌ WRONG - Ref access affects rendering
function MyComponent({ showInput }) {
const inputRef = useRef(null);
return (
<div>
{showInput && (
<input ref={inputRef} />
)}
{/* 🚩 Reading ref in render expression */}
<div>Value: {inputRef.current?.value || "empty"}</div>
</div>
);
}
// ✅ CORRECT - Store ref value in state if needed for rendering
function MyComponent({ showInput }) {
const inputRef = useRef(null);
const [inputValue, setInputValue] = useState("");
function handleChange(e) {
setInputValue(e.target.value);
}
return (
<div>
{showInput && (
<input ref={inputRef} onChange={handleChange} />
)}
{/* ✅ Using state instead of ref for rendering */}
<div>Value: {inputValue || "empty"}</div>
</div>
);
}If you need a value for rendering, it should be in state, not a ref.
In development with React Strict Mode, components render twice. Ensure your ref logic handles this:
function MyComponent() {
const renderCountRef = useRef(0);
// This will increment twice in Strict Mode dev
renderCountRef.current += 1;
console.log(`Render count: ${renderCountRef.current}`);
// In Strict Mode: logs 1, then 2
// ✅ Better: Use effect for side effects
useEffect(() => {
// This runs once after mount in dev too
console.log("Effect ran");
}, []);
return <div>Component</div>;
}
// Enable Strict Mode in your app:
// index.js or main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>
);Strict Mode helps catch side effects during rendering but can cause ref values to update unexpectedly.
The useRef hook is often misunderstood as a way to "escape" React's rendering model, but it's actually tightly integrated with React's lifecycle. When you call useRef, React creates a ref object that's associated with the specific component instance. This ref object persists across re-renders because React maintains a reference to it in its internal fiber tree.
One subtle aspect is that refs are not truly "outside" of React's control - they're part of the component instance. This is why accessing them during rendering causes issues: React needs to complete the render phase before side effects (including ref mutations) can be safely applied.
For complex scenarios where you need to coordinate refs across multiple components or manage refs in libraries, consider using the useImperativeHandle hook to expose specific methods from child components, or create custom hooks that manage ref logic internally. Remember that refs are primarily for imperative DOM operations and storing mutable values that don't affect the visual output.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite