Lifecycle Hooks
This page explains how Lifecycle (src/model/Lifecycle.js) manages the six hooks, the update debounce, and one hook that is not currently wired up anywhere in the source.
The Six Hooks
Lifecycle receives a when config object at construction, mapping to six callbacks:
| Hook | Fires | Can return false to cancel? |
|---|---|---|
beforeRender |
Before the initial render | Yes |
rendered |
After the initial render completes | No (always runs) |
beforeUpdate |
Before a data update (after the 300ms debounce) | Yes |
updated |
After a data update completes | No (always runs) |
beforeDestroy |
Before destroy | Yes |
destroyed |
After destroy completes | No (always runs) |
Only the three before* hooks can cancel the following action by returning false (#beforeAction, Lifecycle.js:22-26); rendered / updated / destroyed always run once their phase completes — there's no cancellation for them.
new JSONEditor({
id: "editor",
when: {
beforeRender: () => {
// return false to cancel this render
return true;
},
rendered: () => console.log("Render complete"),
beforeUpdate: () => true, // return false to cancel this update
updated: () => console.log("Data updated:", editor.json),
},
});
render(): the Initial Render
After JSONEditor finishes construction and mounts the DOM, it calls lifecycle.render(callback) exactly once. The sequence:
- Records
#startAt = Date.now() - Runs
beforeRenderCallback; if it returnsfalse, the method returns immediately —callbacknever runs, andrenderednever fires await callback()(the actual node tree render)- Logs
Rendered in {ms}ms.to the console - If
renderedCallbackis set, runs it
update(): the 300ms Debounce
Every node data change (editing a value, switching a type) calls lifecycle.update(callback). It debounces via setTimeout paired with clearTimeout:
async update(cb) {
clearTimeout(this.#updateTimer);
this.#updateTimer = setTimeout(async () => {
// beforeUpdate → callback → updated
}, 300);
}
As a result, continuous input (e.g. typing character-by-character in a textarea) only fires beforeUpdate / updated once, 300ms after input stops — every intermediate call to update() resets the timer and cancels the previously scheduled run. If beforeUpdate returns false, that scheduled update is skipped and updated never runs; the next data change still reschedules normally.
destroy(): Not Currently Invoked Automatically
Lifecycle.destroy(cb) is fully implemented (running beforeDestroy → cb() → destroyed in sequence), but nothing in src/model/JSONEditor.js currently calls lifecycle.destroy(). This means setting when.beforeDestroy / when.destroyed will not fire from any of the existing public API operations (import / reset / enable / disable). Unless your application obtains the Lifecycle instance itself and calls it manually — and there is currently no public accessor, since Lifecycle is stored in JSONEditor's private field #lifecycle — these two hooks will not run in the current version.
Further Reading
- The node tree data model and type system: Core Concepts
- The constructor's full
when.*parameter table: API Reference