Lifecycle
When components and modules become active, when their render loop event methods tick, when they're destroyed — and the order it all happens in.
Two kinds of entities share the same set of named event methods:
| Kind | What it is |
|---|---|
Module (ContextModule) | Global system. Always on once registered. |
Component (Object3DBehaviour) | Behaviour added to a single Object3D via addComponent(). Can be enabled / disabled / destroyed. |
Modules can't be destroyed or disabled/enabled. Everything else they share with components.
Bootstrap
start() of ThreeStart instance is called once and it starts lifecycle.
- Modules first, in
onAwake→onStartorder, fully finished before any component lifecycle starts. Once a component'sonAwakeruns, every module has already gone through both phases — sothis.modules.<key>is safe to use fromonAwakeonwards. - Then components, in
onAwake→onEnable→onStartorder. onEnableandonStartfire only on active components. A component is active when it isenabledAND no ancestor was made inactive viasetActive(ancestor, false). Inactive components stop atonAwakeand wait — the moment they become active,onEnable→onStartfires for them.- Inside one phase, the order matches registration / scene-graph order. Modules run in the order you passed to
addModules(). Components run in scene-graph traversal order.
Bootstrap touches only:
- Modules registered on your
ThreeStartinstance viaaddModules(). - Components added via
addComponent()to objects that were added to the scene tree of yourThreeStartinstance's context (ctx).
If a module's onAwake / onStart spawns objects with components, those components don't become active mid-bootstrap — they wait their turn.
Render loop
Also known as the animation loop. Each frame, in order:
In every render loop event method, all modules run before any component. A module can mutate state in onUpdate and every component sees the result the same frame.
Render loop event methods are subscribed only if you override them. An empty component pays nothing for dispatch.
When a component starts its lifecycle
A component starts its lifecycle the moment all three of these are true:
- The component was added to an
Object3DviaaddComponent(obj, MyComp). - That object was added to the scene tree of your
ThreeStartinstance's context (ctx.scene). -
ThreeStart.start()was called.
The order in which you do these doesn't matter. You can call addComponent before start() or after; you can add the component to an object that's already in the scene tree, or to one that gets added later. Whichever route you take, the moment the last of the three holds, the component becomes active and runs its first-activation event methods.
const starter = new ThreeStart();
const obj = new THREE.Object3D();
addComponent(obj, MyComp);
starter.ctx.scene.add(obj);
starter.start(); // MyComp: onAwake -> onStart -> onEnable -> render loop eventsconst starter = new ThreeStart();
const obj = new THREE.Object3D();
starter.ctx.scene.add(obj);
starter.start();
addComponent(obj, MyComp); // MyComp: onAwake -> onStart -> onEnable -> render loop eventsconst starter = new ThreeStart();
const obj = new THREE.Object3D();
starter.start();
addComponent(obj, MyComp);
starter.ctx.scene.add(obj); // MyComp: onAwake -> onStart -> onEnable -> render loop eventsEnable/Disable an alive component
Component's enable() / disable() methods fire the matching event method and flip enabled.
While a component is disabled, its render loop event methods (onUpdate / onBeforeRender / onAfterRender) don't fire. They resume on the next enable().
setActive(obj, ...) toggles whole subtrees — fires onEnable / onDisable on every component inside. The object's active state takes priority over the component's own enabled: an enabled component on an inactive-in-hierarchy object sits idle. By default every object is active; setActive is the opt-in switch for parts of the hierarchy. See the setActive guide for the full story.
Destroy
Pass a component to destroy(comp) to destroy it:
onDisable()(if it was enabled) →onDestroy()fires.- The lifecycle stops; the component unsubscribes from the render loop.
- The component is removed from the object's component list.
Pass an Object3D to destroy(obj) to destroy every component on obj and on its whole subtree. Order: scene-graph traversal (the object itself first, then each descendant), and on each object — components in the order they were added. After every component is destroyed, the object is removed from its parent.
Cheat sheet
| Hook | When | Component | Module |
|---|---|---|---|
onAwake | Once, on first activation | ✓ | ✓ |
onStart | Once, after onAwake (and after onEnable on components) | ✓ | ✓ |
onEnable | Each entry into Active | ✓ | — |
onDisable | Each exit from Active | ✓ | — |
onUpdate | Each frame, before render | ✓ | ✓ |
onBeforeRender | Each frame, just before draw | ✓ | ✓ |
onAfterRender | Each frame, just after draw | ✓ | ✓ |
onDestroy | Once, on destroy(...) | ✓ | — |