The Problem
You need a parent component to call methods on a child component or access its DOM node, but forwardRef is not working -- the ref is always null or the methods are undefined.
Symptoms
ref.currentis null when trying to call child methods- TypeScript error: "Property does not exist on type ForwardedRef"
- Parent component gets "ref.focus is not a function"
- Ref works on native elements but not on custom components
The Root Cause
React function components do not have instances by default. You must use forwardRef to accept a ref, and useImperativeHandle to expose specific methods.
Real Error Scenario
```javascript // PARENT function Form() { const inputRef = useRef(null);
const handleSubmit = () => { inputRef.current.focus(); // ERROR: focus is not a function };
return <CustomInput ref={inputRef} />; }
// CHILD (WRONG: ref is not forwarded) function CustomInput() { return <input type="text" />; } ```
How to Fix It
Fix 1: Use forwardRef Properly
```javascript import { forwardRef, useRef, useImperativeHandle } from 'react';
const CustomInput = forwardRef((props, ref) => { const inputRef = useRef(null);
useImperativeHandle(ref, () => ({ focus: () => inputRef.current?.focus(), clear: () => { inputRef.current.value = ''; }, getValue: () => inputRef.current?.value || '' }));
return <input ref={inputRef} type="text" {...props} />; });
// Parent can now call: // inputRef.current.focus() // inputRef.current.clear() // inputRef.current.getValue() ```
Fix 2: TypeScript with Proper Types
```typescript interface InputHandle { focus: () => void; clear: () => void; getValue: () => string; }
const CustomInput = forwardRef<InputHandle, InputProps>((props, ref) => { const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({ focus: () => inputRef.current?.focus(), clear: () => { inputRef.current.value = ''; }, getValue: () => inputRef.current?.value || '' }));
return <input ref={inputRef} type="text" {...props} />; }); ```
Fix 3: Forward Ref to Third-Party Components
// If using a library component that doesn't forward refs,
// wrap it and attach a ref manually
const WrappedSelect = forwardRef((props, ref) => {
const containerRef = useRef(null);
useImperativeHandle(ref, () => ({
element: containerRef.current
}));
return <div ref={containerRef}><ThirdPartySelect {...props} /></div>;
});Common Mistakes
- Forgetting to wrap the component with
forwardRef - Not passing the ref to an actual DOM element or class component
- Exposing too much via
useImperativeHandle(prefer declarative APIs) - Using
refas a regular prop instead of the second argument offorwardRef