Event API ​
BlockSuite constructs a block tree using Workspace
, Page
, and Block
, which can be used for framework agnostic state management. Once the block tree nodes are bound to a framework, the block content can be rendered. It is also necessary to subscribe to corresponding events when blocks are updated, in order to refresh the UI framework on demand.
Using Slots ​
BlockSuite extensively uses Slot
to manage events. You can think of it as a type-safe event emitter or a simplified RxJS Observable:
import { Slot } from '@blocksuite/store';
// Create a new slot
const slot = new Slot<{ name: string }>();
// Subscribe events
slot.on(({ name }) => console.log(name));
// Or alternatively only listen event once
slot.once(({ name }) => console.log(name));
// Emit the event
slot.emit({ name: 'foo' });
import { Slot } from '@blocksuite/store';
// Create a new slot
const slot = new Slot<{ name: string }>();
// Subscribe events
slot.on(({ name }) => console.log(name));
// Or alternatively only listen event once
slot.once(({ name }) => console.log(name));
// Emit the event
slot.emit({ name: 'foo' });
To unsubscribe from the slot, simply use the return value of slot.on()
:
const slot = new Slot();
const disposable = slot.on(myHandler);
// Dispose the subscription
disposable.dispose();
const slot = new Slot();
const disposable = slot.on(myHandler);
// Dispose the subscription
disposable.dispose();
Subscribing Block Events ​
Under the page
instance, you can subscribe to common events using page.slots
:
page.slots.rootAdded.on(() => {
// The `page.root` is not null at this point
// You can bind it to a component tree now
console.log('rootAdded!');
});
page.addBlock('affine:page'); // rootAdded!
page.slots.rootAdded.on(() => {
// The `page.root` is not null at this point
// You can bind it to a component tree now
console.log('rootAdded!');
});
page.addBlock('affine:page'); // rootAdded!
Moreover, for any node in the block tree, events can be triggered when the node is updated:
const model = page.root[0];
// Triggered when the `props` of the block model is updated
model.propsUpdated.on(() => updateMyComponent());
// Triggered when the `children` of the block model is updated
model.childrenUpdated.on(() => updateMyComponent());
const model = page.root[0];
// Triggered when the `props` of the block model is updated
model.propsUpdated.on(() => updateMyComponent());
// Triggered when the `children` of the block model is updated
model.childrenUpdated.on(() => updateMyComponent());
In the prebuilt AFFiNE editor, which is based on the lit framework, the UI component of each block subscribes to its model updates using this pattern.