);
}
export default Card;
```
**Changes Made:**
1. ✅ Changed import from `forwardRef` to `Ref` type
2. ✅ Added `ref?: Ref` to interface
3. ✅ Removed `forwardRef` wrapper
4. ✅ Added `ref` to props destructuring
5. ✅ Removed `displayName`
## Example 3: Input with useImperativeHandle
**Before:**
```javascript
import { forwardRef, useRef, useImperativeHandle } from 'react';
const SearchInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus() {
inputRef.current?.focus();
},
clear() {
inputRef.current.value = '';
},
getValue() {
return inputRef.current?.value || '';
}
}));
return (
);
});
export default SearchInput;
```
**After:**
```javascript
import { useRef, useImperativeHandle } from 'react';
function SearchInput({ ref, ...props }) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus() {
inputRef.current?.focus();
},
clear() {
inputRef.current.value = '';
},
getValue() {
return inputRef.current?.value || '';
}
}));
return (
);
}
export default SearchInput;
```
**Usage (unchanged):**
```javascript
function SearchBar() {
const searchRef = useRef();
const handleClear = () => {
searchRef.current?.clear();
};
return (
<>
>
);
}
```
## Example 4: Component Library Pattern
**Before:**
```typescript
import { forwardRef, ComponentPropsWithoutRef, ElementRef } from 'react';
type ButtonElement = ElementRef<'button'>;
type ButtonProps = ComponentPropsWithoutRef<'button'> & {
variant?: 'primary' | 'secondary';
};
const Button = forwardRef(
({ variant = 'primary', className, ...props }, ref) => {
return (
);
}
);
Button.displayName = 'Button';
```
**After:**
```typescript
import { Ref, ComponentPropsWithoutRef, ElementRef } from 'react';
type ButtonElement = ElementRef<'button'>;
type ButtonProps = ComponentPropsWithoutRef<'button'> & {
variant?: 'primary' | 'secondary';
ref?: Ref;
};
function Button({
variant = 'primary',
className,
ref,
...props
}: ButtonProps) {
return (
);
}
```
## MUST
- Add `ref` to props interface when using TypeScript
- Use `Ref` type from React for TypeScript
- Test that ref forwarding works after migration
- Maintain component behavior exactly (only syntax changes)
## SHOULD
- Migrate components gradually (forwardRef still works)
- Update tests to verify ref behavior
- Use consistent prop ordering (ref near other element props)
- Document breaking changes if part of public API
## NEVER
- Remove `forwardRef` if still on React 18
- Change component behavior during migration
- Break existing ref usage in parent components
- Skip TypeScript type updates for ref prop
## After Migration
1. **Verify Ref Forwarding**:
```javascript
const ref = useRef(null);
// ref.current should be the DOM element
```
2. **Check TypeScript Compilation**:
```bash
npx tsc --noEmit
```
No errors about ref props
3. **Test Component Behavior**:
- Component renders correctly
- Ref accesses correct DOM element
- useImperativeHandle methods work (if used)
- No console warnings about deprecated APIs
4. **Verify Backward Compatibility**:
- Existing usage still works
- No breaking changes to component API
- Tests pass
---
## Migration Checklist
When migrating a component from forwardRef:
- [ ] Remove `forwardRef` import
- [ ] Remove `forwardRef` wrapper function
- [ ] Add `ref` to props destructuring
- [ ] Add `ref` type to TypeScript interface (if applicable)
- [ ] Remove `displayName` if only used for forwardRef
- [ ] Test ref forwarding works
- [ ] Update component tests
- [ ] Check TypeScript compilation
- [ ] Verify no breaking changes to API
## Common Migration Patterns
### Pattern 1: Simple Ref Forwarding
```javascript
// Before
const Comp = forwardRef((props, ref) =>
);
// After
function Comp({ ref }) { return ; }
```
### Pattern 2: With useImperativeHandle
```javascript
// Before
const Comp = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({ method() {} }));
return ;
});
// After
function Comp({ ref }) {
useImperativeHandle(ref, () => ({ method() {} }));
return ;
}
```
### Pattern 3: TypeScript with Generics
```typescript
// Before
const Comp = forwardRef((props, ref) => ...);
// After
function Comp({ ref, ...props }: Props & { ref?: Ref }) { ... }
```
For comprehensive forwardRef migration documentation, see: `research/react-19-comprehensive.md` lines 978-1033.
## Ref Cleanup Functions (New in React 19)
React 19 supports cleanup functions in ref callbacks:
```javascript
{
console.log('Connected:', node);
return () => {
console.log('Disconnected:', node);
};
}}
/>
```
**When Cleanup Runs:**
- Component unmounts
- Ref changes to different element
This works with both ref-as-prop and the old forwardRef pattern.