This warning appears when you try to use string refs (like ref="myRef") in a React function component. String refs are a legacy API that only works with class components, and React function components require the useRef hook or callback refs instead.
This warning occurs because string refs are an older React API pattern that was designed for class components. In class components, you could write `ref="myInput"` and later access the DOM element via `this.refs.myInput`. However, function components don't have a `this` context or instance properties, so they cannot support this pattern. String refs have been considered legacy since React 16.3 (released in 2018) and are likely to be removed in future React versions. React recommends migrating to either the `useRef` hook for function components or callback refs for more complex scenarios. While string refs aren't officially deprecated yet, they're not composable, can lead to hard-to-debug issues when the same string ref is used in multiple places, and don't work with modern React patterns like hooks and function components.
Import the useRef hook from React and create a ref object instead of using a string:
// ❌ Before (causes warning)
function MyComponent() {
return <input ref="myInput" />;
}
// ✅ After (correct approach)
import { useRef } from 'react';
function MyComponent() {
const myInputRef = useRef(null);
return <input ref={myInputRef} />;
}The ref object will have a .current property that holds the DOM element once mounted.
Change any code that accesses the ref from the old this.refs pattern to the new .current property:
// ❌ Before (class component pattern)
class MyComponent extends React.Component {
handleClick = () => {
this.refs.myInput.focus();
}
render() {
return (
<>
<input ref="myInput" />
<button onClick={this.handleClick}>Focus</button>
</>
);
}
}
// ✅ After (function component with useRef)
import { useRef } from 'react';
function MyComponent() {
const myInputRef = useRef(null);
const handleClick = () => {
myInputRef.current?.focus();
};
return (
<>
<input ref={myInputRef} />
<button onClick={handleClick}>Focus</button>
</>
);
}Note the optional chaining (?.) to safely access .current in case the ref isn't attached yet.
For dynamic refs or when you need to perform side effects when the ref changes, use callback refs:
import { useState, useCallback } from 'react';
function MyComponent() {
const [inputElement, setInputElement] = useState(null);
// Callback ref - called when element mounts/unmounts
const callbackRef = useCallback((element) => {
if (element) {
setInputElement(element);
element.focus(); // Perform side effect when mounted
}
}, []);
return <input ref={callbackRef} />;
}Callback refs receive the DOM element (or null on unmount) as an argument and give you more control over when refs are set and unset.
If you're building a reusable component that needs to expose a ref to its parent, use React.forwardRef:
import { forwardRef } from 'react';
// ✅ Allows parent to pass ref to underlying input
const CustomInput = forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
// Usage in parent component
function ParentComponent() {
const inputRef = useRef(null);
return (
<>
<CustomInput ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>
Focus Input
</button>
</>
);
}Without forwardRef, function components cannot receive refs as props.
React 19 Changes: React 19 adds support for function components to receive refs directly without forwardRef, but string refs remain unsupported and should still be avoided.
Multiple Refs: If you need to track multiple dynamic elements (like list items), use an array or Map with callback refs:
function ItemList({ items }) {
const itemRefs = useRef(new Map());
return items.map((item) => (
<div
key={item.id}
ref={(el) => {
if (el) {
itemRefs.current.set(item.id, el);
} else {
itemRefs.current.delete(item.id);
}
}}
>
{item.name}
</div>
));
}ESLint Rule: Enable react/no-string-refs in your ESLint configuration to catch string refs during development:
{
"rules": {
"react/no-string-refs": "error"
}
}Performance Consideration: useRef doesn't cause re-renders when its .current value changes. If you need to trigger re-renders based on ref changes, use callback refs with state as shown in Step 3.
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