or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

component-system.mdcore-rendering.mdevent-handling.mdfragments-utilities.mdindex.mdrefs.mdvnode-creation.md
tile.json

refs.mddocs/

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