0
# Refs and Forward Refs
1
2
Reference system for accessing DOM elements and component instances, with forward ref support for passing refs through component boundaries.
3
4
## Capabilities
5
6
### Create Ref
7
8
Creates a ref object for accessing DOM elements or component instances.
9
10
```typescript { .api }
11
/**
12
* Creates a ref object for accessing DOM elements or component instances
13
* @returns RefObject with current property initially set to null
14
*/
15
function createRef<T = Element>(): RefObject<T>;
16
17
interface RefObject<T> {
18
readonly current: T | null;
19
}
20
```
21
22
**Usage Examples:**
23
24
```typescript
25
import { createRef, Component, createVNode, VNodeFlags } from "inferno";
26
27
class TextInputComponent extends Component {
28
constructor(props) {
29
super(props);
30
this.inputRef = createRef<HTMLInputElement>();
31
}
32
33
focusInput = () => {
34
if (this.inputRef.current) {
35
this.inputRef.current.focus();
36
}
37
};
38
39
componentDidMount() {
40
// Access DOM element after mount
41
if (this.inputRef.current) {
42
console.log('Input element:', this.inputRef.current);
43
}
44
}
45
46
render() {
47
return createVNode(VNodeFlags.HtmlElement, 'div', null, [
48
createVNode(VNodeFlags.InputElement, 'input', null, null, ChildFlags.HasInvalidChildren, {
49
ref: this.inputRef,
50
type: 'text',
51
placeholder: 'Enter text'
52
}),
53
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Focus Input', ChildFlags.HasInvalidChildren, {
54
onClick: this.focusInput
55
})
56
]);
57
}
58
}
59
```
60
61
### Forward Ref
62
63
Creates a component that forwards refs to child components or DOM elements.
64
65
```typescript { .api }
66
/**
67
* Creates a component that forwards refs to child components or DOM elements
68
* @param render - Render function that receives props and ref
69
* @returns Component that can accept and forward refs
70
*/
71
function forwardRef<T = any, P = Props<any>>(
72
render: (
73
props: Readonly<{ children?: InfernoNode }> & Readonly<P>,
74
ref: RefObject<T>
75
) => InfernoNode
76
): any;
77
```
78
79
**Usage Examples:**
80
81
```typescript
82
import { forwardRef, createRef, createVNode, VNodeFlags } from "inferno";
83
84
// Forward ref component for custom input
85
const CustomInput = forwardRef<HTMLInputElement, { placeholder?: string }>((props, ref) => {
86
return createVNode(VNodeFlags.InputElement, 'input', 'custom-input', null, ChildFlags.HasInvalidChildren, {
87
ref: ref,
88
type: 'text',
89
placeholder: props.placeholder
90
});
91
});
92
93
class ParentComponent extends Component {
94
constructor(props) {
95
super(props);
96
this.customInputRef = createRef<HTMLInputElement>();
97
}
98
99
handleClick = () => {
100
if (this.customInputRef.current) {
101
this.customInputRef.current.focus();
102
this.customInputRef.current.value = 'Focused!';
103
}
104
};
105
106
render() {
107
return createVNode(VNodeFlags.HtmlElement, 'div', null, [
108
createComponentVNode(VNodeFlags.ForwardRefComponent, CustomInput, {
109
ref: this.customInputRef,
110
placeholder: 'Custom input'
111
}),
112
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Focus Custom Input', ChildFlags.HasInvalidChildren, {
113
onClick: this.handleClick
114
})
115
]);
116
}
117
}
118
```
119
120
### Mount Ref
121
122
Internal function for mounting refs (used by the rendering system).
123
124
```typescript { .api }
125
/**
126
* Internal function for mounting refs during the rendering process
127
* @param ref - Ref callback or RefObject
128
* @param value - Value to assign to the ref
129
* @param lifecycle - Array of lifecycle functions to execute
130
*/
131
function mountRef(ref: any, value: any, lifecycle: Array<() => void>): void;
132
```
133
134
### Unmount Ref
135
136
Internal function for unmounting refs (used by the rendering system).
137
138
```typescript { .api }
139
/**
140
* Internal function for unmounting refs during component cleanup
141
* @param ref - Ref callback or RefObject to unmount
142
*/
143
function unmountRef(ref: any): void;
144
```
145
146
## Ref Types
147
148
### Ref Callback
149
150
```typescript { .api }
151
type Ref<T = Element> = {
152
bivarianceHack(instance: T | null): any;
153
}['bivarianceHack'];
154
```
155
156
### Forward Ref Interface
157
158
```typescript { .api }
159
interface ForwardRef<P, T> extends Inferno.StatelessComponent<P> {
160
ref: Ref<T>;
161
}
162
```
163
164
## Ref Usage Patterns
165
166
### DOM Element Access
167
168
```typescript
169
class ScrollableComponent extends Component {
170
constructor(props) {
171
super(props);
172
this.containerRef = createRef<HTMLDivElement>();
173
}
174
175
scrollToTop = () => {
176
if (this.containerRef.current) {
177
this.containerRef.current.scrollTop = 0;
178
}
179
};
180
181
scrollToBottom = () => {
182
if (this.containerRef.current) {
183
this.containerRef.current.scrollTop = this.containerRef.current.scrollHeight;
184
}
185
};
186
187
render() {
188
return createVNode(VNodeFlags.HtmlElement, 'div', null, [
189
createVNode(VNodeFlags.HtmlElement, 'div', 'scrollable', this.props.children, ChildFlags.UnknownChildren, {
190
ref: this.containerRef,
191
style: { height: '200px', overflow: 'auto' }
192
}),
193
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Scroll to Top', ChildFlags.HasInvalidChildren, {
194
onClick: this.scrollToTop
195
}),
196
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Scroll to Bottom', ChildFlags.HasInvalidChildren, {
197
onClick: this.scrollToBottom
198
})
199
]);
200
}
201
}
202
```
203
204
### Component Instance Access
205
206
```typescript
207
class ChildComponent extends Component {
208
getValue() {
209
return this.state.value;
210
}
211
212
reset() {
213
this.setState({ value: '' });
214
}
215
216
render() {
217
return createVNode(VNodeFlags.InputElement, 'input', null, null, ChildFlags.HasInvalidChildren, {
218
value: this.state.value,
219
onChange: (e) => this.setState({ value: e.target.value })
220
});
221
}
222
}
223
224
class ParentComponent extends Component {
225
constructor(props) {
226
super(props);
227
this.childRef = createRef<ChildComponent>();
228
}
229
230
handleGetValue = () => {
231
if (this.childRef.current) {
232
console.log('Child value:', this.childRef.current.getValue());
233
}
234
};
235
236
handleReset = () => {
237
if (this.childRef.current) {
238
this.childRef.current.reset();
239
}
240
};
241
242
render() {
243
return createVNode(VNodeFlags.HtmlElement, 'div', null, [
244
createComponentVNode(VNodeFlags.ComponentClass, ChildComponent, {
245
ref: this.childRef
246
}),
247
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Get Value', ChildFlags.HasInvalidChildren, {
248
onClick: this.handleGetValue
249
}),
250
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Reset', ChildFlags.HasInvalidChildren, {
251
onClick: this.handleReset
252
})
253
]);
254
}
255
}
256
```
257
258
### Callback Refs
259
260
```typescript
261
class CallbackRefComponent extends Component {
262
setInputRef = (element: HTMLInputElement | null) => {
263
this.inputElement = element;
264
if (element) {
265
console.log('Input element mounted:', element);
266
} else {
267
console.log('Input element unmounted');
268
}
269
};
270
271
focusInput = () => {
272
if (this.inputElement) {
273
this.inputElement.focus();
274
}
275
};
276
277
render() {
278
return createVNode(VNodeFlags.HtmlElement, 'div', null, [
279
createVNode(VNodeFlags.InputElement, 'input', null, null, ChildFlags.HasInvalidChildren, {
280
ref: this.setInputRef,
281
type: 'text'
282
}),
283
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Focus', ChildFlags.HasInvalidChildren, {
284
onClick: this.focusInput
285
})
286
]);
287
}
288
}
289
```
290
291
### Multiple Refs
292
293
```typescript
294
class MultiRefComponent extends Component {
295
constructor(props) {
296
super(props);
297
this.refs = {
298
input1: createRef<HTMLInputElement>(),
299
input2: createRef<HTMLInputElement>(),
300
container: createRef<HTMLDivElement>()
301
};
302
}
303
304
focusFirst = () => {
305
this.refs.input1.current?.focus();
306
};
307
308
focusSecond = () => {
309
this.refs.input2.current?.focus();
310
};
311
312
getAllValues = () => {
313
const values = {
314
input1: this.refs.input1.current?.value || '',
315
input2: this.refs.input2.current?.value || ''
316
};
317
console.log('All values:', values);
318
return values;
319
};
320
321
render() {
322
return createVNode(VNodeFlags.HtmlElement, 'div', null, [
323
createVNode(VNodeFlags.HtmlElement, 'div', 'input-container', [
324
createVNode(VNodeFlags.InputElement, 'input', null, null, ChildFlags.HasInvalidChildren, {
325
ref: this.refs.input1,
326
placeholder: 'Input 1'
327
}),
328
createVNode(VNodeFlags.InputElement, 'input', null, null, ChildFlags.HasInvalidChildren, {
329
ref: this.refs.input2,
330
placeholder: 'Input 2'
331
})
332
], ChildFlags.HasNonKeyedChildren, {
333
ref: this.refs.container
334
}),
335
createVNode(VNodeFlags.HtmlElement, 'div', null, [
336
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Focus First', ChildFlags.HasInvalidChildren, {
337
onClick: this.focusFirst
338
}),
339
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Focus Second', ChildFlags.HasInvalidChildren, {
340
onClick: this.focusSecond
341
}),
342
createVNode(VNodeFlags.HtmlElement, 'button', null, 'Get Values', ChildFlags.HasInvalidChildren, {
343
onClick: this.getAllValues
344
})
345
], ChildFlags.HasNonKeyedChildren)
346
]);
347
}
348
}
349
```
350
351
## Forward Ref Patterns
352
353
### HOC with Forward Ref
354
355
```typescript
356
function withLogging<P>(WrappedComponent: any) {
357
return forwardRef<any, P>((props, ref) => {
358
console.log('Rendering with props:', props);
359
360
return createComponentVNode(
361
VNodeFlags.ComponentClass,
362
WrappedComponent,
363
{ ...props, ref }
364
);
365
});
366
}
367
368
const LoggedInput = withLogging(CustomInput);
369
```
370
371
### Composite Components
372
373
```typescript
374
const InputGroup = forwardRef<HTMLInputElement, { label: string; placeholder?: string }>((props, ref) => {
375
return createVNode(VNodeFlags.HtmlElement, 'div', 'input-group', [
376
createVNode(VNodeFlags.HtmlElement, 'label', null, props.label),
377
createVNode(VNodeFlags.InputElement, 'input', null, null, ChildFlags.HasInvalidChildren, {
378
ref: ref,
379
placeholder: props.placeholder
380
})
381
]);
382
});
383
```
384
385
## Best Practices
386
387
1. **Use createRef() for object refs**: More predictable than callback refs
388
2. **Check ref.current before use**: Always verify the ref is not null
389
3. **Don't overuse refs**: Prefer props and state for data flow
390
4. **Forward refs in reusable components**: Allow parent components to access underlying elements
391
5. **Avoid string refs**: Use object refs or callback refs instead
392
6. **Clean up in unmount**: Remove event listeners attached via refs