0
# React Hooks
1
2
@pixi/react provides React hooks for accessing PixiJS application state, integrating with the ticker system, and dynamically extending components. These hooks follow React patterns and integrate seamlessly with component lifecycle.
3
4
## Capabilities
5
6
### useApplication Hook
7
8
Retrieves the nearest PixiJS Application from the React context.
9
10
```typescript { .api }
11
/**
12
* Retrieves the nearest Pixi.js Application from the Pixi React context
13
* @returns ApplicationState containing the app instance and initialization status
14
* @throws Error if used outside of Application component context
15
*/
16
function useApplication(): ApplicationState;
17
18
interface ApplicationState {
19
/** The PixiJS Application instance */
20
app: PixiApplication;
21
/** Whether the application has finished initialization */
22
isInitialised: boolean;
23
/** Whether the application is currently initializing */
24
isInitialising: boolean;
25
}
26
```
27
28
**Usage Examples:**
29
30
```typescript
31
import { useApplication } from "@pixi/react";
32
import { useEffect, useState } from "react";
33
34
const AppInfoComponent = () => {
35
const { app, isInitialised } = useApplication();
36
const [fps, setFps] = useState(0);
37
38
useEffect(() => {
39
if (!isInitialised) return;
40
41
// Access application properties
42
console.log("Canvas size:", app.canvas.width, app.canvas.height);
43
console.log("Renderer type:", app.renderer.type);
44
45
// Monitor FPS
46
const updateFPS = () => setFps(app.ticker.FPS);
47
app.ticker.add(updateFPS);
48
49
return () => app.ticker.remove(updateFPS);
50
}, [app, isInitialised]);
51
52
if (!isInitialised) {
53
return <pixiText text="Loading..." />;
54
}
55
56
return (
57
<pixiText
58
text={`FPS: ${fps.toFixed(1)}`}
59
x={10}
60
y={10}
61
style={{ fontSize: 14, fill: "white" }}
62
/>
63
);
64
};
65
66
// Access renderer information
67
const RendererInfo = () => {
68
const { app, isInitialised } = useApplication();
69
70
if (!isInitialised) return null;
71
72
const rendererInfo = {
73
type: app.renderer.type,
74
resolution: app.renderer.resolution,
75
backgroundColor: app.renderer.background.color,
76
};
77
78
return (
79
<pixiText
80
text={`Renderer: ${rendererInfo.type}`}
81
x={10}
82
y={30}
83
/>
84
);
85
};
86
```
87
88
### useTick Hook
89
90
Attaches a callback to the application's Ticker for frame-based animations and updates.
91
92
```typescript { .api }
93
/**
94
* Attaches a callback to the application's Ticker
95
* @param options - Either a callback function or options object with callback
96
*/
97
function useTick<T>(options: TickerCallback<T> | UseTickOptions<T>): void;
98
99
type TickerCallback<T> = (ticker: Ticker, context?: T) => void;
100
101
interface UseTickOptions<T> {
102
/** The function to be called on each tick */
103
callback: TickerCallback<T>;
104
/** The value of `this` within the callback */
105
context?: T;
106
/** Whether this callback is currently enabled */
107
isEnabled?: boolean;
108
/** The priority of this callback compared to other callbacks on the ticker */
109
priority?: number;
110
}
111
```
112
113
**Usage Examples:**
114
115
```typescript
116
import { useTick } from "@pixi/react";
117
import { useState, useCallback } from "react";
118
119
// Simple animation with function callback
120
const RotatingSprite = ({ texture }) => {
121
const [rotation, setRotation] = useState(0);
122
123
useTick(useCallback((ticker) => {
124
setRotation(prev => prev + 0.01 * ticker.deltaTime);
125
}, []));
126
127
return (
128
<pixiSprite
129
texture={texture}
130
rotation={rotation}
131
anchor={0.5}
132
x={400}
133
y={300}
134
/>
135
);
136
};
137
138
// Advanced animation with options
139
const AnimatedGraphics = () => {
140
const [scale, setScale] = useState(1);
141
const [isAnimating, setIsAnimating] = useState(true);
142
143
useTick({
144
callback: useCallback((ticker) => {
145
setScale(prev => {
146
const newScale = prev + Math.sin(ticker.lastTime * 0.01) * 0.001;
147
return Math.max(0.5, Math.min(2, newScale));
148
});
149
}, []),
150
isEnabled: isAnimating,
151
priority: 1, // Higher priority than default (0)
152
});
153
154
const drawCallback = useCallback((graphics) => {
155
graphics.clear();
156
graphics.setFillStyle({ color: "blue" });
157
graphics.circle(0, 0, 50);
158
graphics.fill();
159
}, []);
160
161
return (
162
<pixiContainer>
163
<pixiGraphics
164
draw={drawCallback}
165
scale={scale}
166
anchor={0.5}
167
x={200}
168
y={200}
169
/>
170
<pixiText
171
text={isAnimating ? "Click to pause" : "Click to resume"}
172
interactive
173
onPointerTap={() => setIsAnimating(!isAnimating)}
174
x={10}
175
y={10}
176
/>
177
</pixiContainer>
178
);
179
};
180
181
// Context-based animation
182
class AnimationContext {
183
constructor(public speed: number = 1) {}
184
185
updateRotation(rotation: number, ticker: Ticker): number {
186
return rotation + 0.02 * this.speed * ticker.deltaTime;
187
}
188
}
189
190
const ContextAnimation = () => {
191
const [rotation, setRotation] = useState(0);
192
const [context] = useState(() => new AnimationContext(2));
193
194
useTick({
195
callback: function(ticker) {
196
// `this` refers to the context object
197
setRotation(prev => this.updateRotation(prev, ticker));
198
},
199
context,
200
});
201
202
return (
203
<pixiSprite
204
texture={texture}
205
rotation={rotation}
206
anchor={0.5}
207
/>
208
);
209
};
210
```
211
212
### useExtend Hook
213
214
Hook version of the extend function for dynamically exposing PixiJS components.
215
216
```typescript { .api }
217
/**
218
* Hook version of extend function for exposing Pixi.js components
219
* @param objects - Object mapping component names to PixiJS constructor classes
220
*/
221
function useExtend(objects: Parameters<typeof extend>[0]): void;
222
```
223
224
**Usage Examples:**
225
226
```typescript
227
import { useExtend } from "@pixi/react";
228
import { useMemo, useState, useEffect } from "react";
229
230
// Dynamic component loading
231
const DynamicComponentLoader = ({ enableAdvanced }) => {
232
const [advancedComponents, setAdvancedComponents] = useState(null);
233
234
useEffect(() => {
235
if (enableAdvanced) {
236
import("pixi.js").then(({ NineSlicePlane, SimplePlane, TilingSprite }) => {
237
setAdvancedComponents({ NineSlicePlane, SimplePlane, TilingSprite });
238
});
239
}
240
}, [enableAdvanced]);
241
242
// Extend components when they become available
243
useExtend(advancedComponents || {});
244
245
if (!enableAdvanced || !advancedComponents) {
246
return <pixiText text="Basic mode" />;
247
}
248
249
return (
250
<pixiContainer>
251
<pixiTilingSprite
252
texture={texture}
253
width={200}
254
height={200}
255
/>
256
<pixiNineSlicePlane
257
texture={buttonTexture}
258
leftWidth={10}
259
rightWidth={10}
260
topHeight={10}
261
bottomHeight={10}
262
/>
263
</pixiContainer>
264
);
265
};
266
267
// Conditional extension based on props
268
const ConditionalExtender = ({ includeFilters, includeMesh }) => {
269
const components = useMemo(() => {
270
const result = {};
271
272
if (includeFilters) {
273
import("pixi.js").then(({ BlurFilter, ColorMatrixFilter }) => {
274
Object.assign(result, { BlurFilter, ColorMatrixFilter });
275
});
276
}
277
278
if (includeMesh) {
279
import("pixi.js").then(({ Mesh, PlaneGeometry }) => {
280
Object.assign(result, { Mesh, PlaneGeometry });
281
});
282
}
283
284
return result;
285
}, [includeFilters, includeMesh]);
286
287
useExtend(components);
288
289
return (
290
<pixiContainer>
291
{includeFilters && (
292
<pixiText text="Filters available" />
293
)}
294
{includeMesh && (
295
<pixiText text="Mesh components available" y={20} />
296
)}
297
</pixiContainer>
298
);
299
};
300
301
// Plugin integration with hooks
302
const PluginIntegration = () => {
303
const [pluginComponents, setPluginComponents] = useState({});
304
305
useEffect(() => {
306
// Dynamically load plugin components
307
Promise.all([
308
import("pixi-spine").then(m => ({ Spine: m.Spine })),
309
import("pixi-viewport").then(m => ({ Viewport: m.Viewport })),
310
]).then(([spine, viewport]) => {
311
setPluginComponents({ ...spine, ...viewport });
312
});
313
}, []);
314
315
useExtend(pluginComponents);
316
317
const hasSpine = 'Spine' in pluginComponents;
318
const hasViewport = 'Viewport' in pluginComponents;
319
320
return (
321
<pixiContainer>
322
{hasViewport && hasSpine && (
323
<pixiViewport
324
screenWidth={800}
325
screenHeight={600}
326
worldWidth={1200}
327
worldHeight={900}
328
>
329
<pixiSpine spineData={spineData} />
330
</pixiViewport>
331
)}
332
</pixiContainer>
333
);
334
};
335
```
336
337
## Hook Rules and Best Practices
338
339
### Dependency Management
340
341
```typescript
342
// ✓ Stable callback reference
343
const RotatingSprite = () => {
344
const [rotation, setRotation] = useState(0);
345
346
const tickCallback = useCallback((ticker) => {
347
setRotation(prev => prev + 0.01 * ticker.deltaTime);
348
}, []); // Empty dependencies - callback is stable
349
350
useTick(tickCallback);
351
352
return <pixiSprite rotation={rotation} />;
353
};
354
355
// ✗ Avoid recreating callback on every render
356
const BadExample = () => {
357
const [rotation, setRotation] = useState(0);
358
359
// This creates a new function on every render!
360
useTick((ticker) => {
361
setRotation(prev => prev + 0.01 * ticker.deltaTime);
362
});
363
364
return <pixiSprite rotation={rotation} />;
365
};
366
```
367
368
### Conditional Hook Usage
369
370
```typescript
371
// ✓ Use isEnabled option for conditional ticking
372
const ConditionalAnimation = ({ shouldAnimate }) => {
373
useTick({
374
callback: useCallback((ticker) => {
375
// Animation logic
376
}, []),
377
isEnabled: shouldAnimate, // Control via options
378
});
379
380
return <pixiSprite />;
381
};
382
383
// ✗ Don't conditionally call hooks
384
const BadConditional = ({ shouldAnimate }) => {
385
if (shouldAnimate) {
386
useTick(() => {}); // Violates rules of hooks!
387
}
388
389
return <pixiSprite />;
390
};
391
```
392
393
## Error Handling
394
395
The hooks include comprehensive error handling:
396
397
- **useApplication**: Throws meaningful error if used outside Application context
398
- **useTick**: Validates callback function and handles ticker lifecycle
399
- **useExtend**: Validates component objects and handles registration failures
400
- All hooks properly clean up resources during component unmounting