This error occurs when using deprecated string refs in React function components. String refs are legacy syntax that React removed in favor of useRef() hook and createRef() API for better type safety and composability.
String refs are an older API where the ref attribute accepts a string value, like ref="textInput", and the DOM node is accessed via this.refs.textInput. This pattern was deprecated in March 2018 (React v16.3.0) because it has several fundamental issues. The primary problems with string refs include: they don't work with static analysis tools like Flow or TypeScript, they can lead to hard-to-debug bugs when the same string ref is accidentally used in multiple places (causing them to overwrite each other), they require React to keep track of the currently executing component which impacts performance, and they don't compose well in modern React patterns. React recommends migrating to either callback refs or the createRef/useRef APIs, which provide better type safety, performance, and debugging capabilities. In React 19 and beyond, string refs will be completely removed from the framework.
Search for string refs in your project to understand the scope of changes needed:
# Search for ref=" patterns
grep -r 'ref="[^{]' src/
# Or use your editor's search functionality
# Look for patterns like: ref="myRef"Common locations include form inputs, DOM node references, and third-party component integrations.
Replace string refs with callback refs that assign to instance properties:
// Before (deprecated)
class MyComponent extends React.Component {
handleClick = () => {
this.refs.myInput.focus();
}
render() {
return <input ref="myInput" />;
}
}
// After (callback ref)
class MyComponent extends React.Component {
handleClick = () => {
this.myInput.focus();
}
render() {
return <input ref={el => this.myInput = el} />;
}
}Callback refs receive the element as an argument and you can store it however you like.
For cleaner class component code, use the createRef API introduced in React 16.3:
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myInput = React.createRef();
}
handleClick = () => {
// Access via .current property
this.myInput.current.focus();
}
render() {
return <input ref={this.myInput} />;
}
}Note that createRef refs are accessed via the .current property.
For function components, use the useRef hook instead:
import React, { useRef } from 'react';
function MyComponent() {
const myInput = useRef(null);
const handleClick = () => {
myInput.current.focus();
};
return (
<div>
<input ref={myInput} />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}The useRef hook is the standard approach for function components and works with TypeScript.
Replace all instances of this.refs.refName with the new ref pattern:
// Before
this.refs.myInput.value
this.refs.myDiv.scrollTop
// After (callback ref)
this.myInput.value
this.myDiv.scrollTop
// After (createRef/useRef)
this.myInput.current.value
this.myDiv.current.scrollTopRemember that createRef and useRef require accessing the .current property.
Check if any dependencies are causing the warnings:
# Update all dependencies to latest versions
npm update
# Or check for specific outdated packages
npm outdated
# Update specific packages
npm install react-component-name@latestIf a library hasn't been updated, consider filing an issue or finding an alternative package.
Add the react/no-string-refs rule to your ESLint configuration:
// .eslintrc.json
{
"extends": ["react-app"],
"rules": {
"react/no-string-refs": "error"
}
}This ensures string refs won't be accidentally introduced in new code.
When working with forwarded refs in custom components, you need to use React.forwardRef to properly pass refs through component boundaries. This is particularly important when migrating component libraries from string refs.
For TypeScript projects, createRef and useRef provide much better type inference than string refs ever could. You can type refs like: const myRef = useRef<HTMLInputElement>(null).
In React Native, string refs follow the same deprecation timeline as React for web. The migration path is identical, though you'll be working with native component refs like TextInput instead of DOM elements.
If you're maintaining a large legacy codebase, consider using a codemod tool to automate the migration. The React team has provided codemods for common ref patterns, though manual review is still recommended for complex cases.
For teams upgrading to React 19, note that string refs have been completely removed and will cause runtime errors rather than just warnings. Plan your migration accordingly before upgrading major versions.
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