docs/api/core/plate-controller.mdx
PlateController is an optional provider component that facilitates accessing specific Plate Stores from outside their respective Plate components.
null
</APIItem>
[]
</APIItem>
{}
</APIItem>
PlateController can be used to access the store of a specific editor using its id. Note that if a matching editor cannot be found, an immutable fallback editor will be returned instead.
const App = withHoc(PlateController, () => {
const mainEditor = useEditorRef('main');
useEffect(() => {
if (!mainEditor.meta.isFallback) {
console.info('Editor mounted', mainEditor);
}
}, [mainEditor]);
return (
<>
<Plate editor={createPlateEditor({ id: 'main' })}>
<PlateContent />
</Plate>
<Plate editor={createPlateEditor({ id: 'secondary' })}>
<PlateContent />
</Plate>
</>
);
});
If hooks like useEditorRef are used inside a PlateController without an explicit id, they will resolve to the currently active editor.
The active editor is determined as follows:
const App = withHoc(PlateController, () => {
const activeEditorId = useEditorId();
const isFallback = !useEditorMounted();
const message = isFallback
? 'Please focus an editor'
: `Active editor: ${activeEditorId}`;
return (
<main>
<p>{message}</p>
<Plate editor={createPlateEditor({ id: 'main', primary: false })}>
<PlateContent />
</Plate>
<Plate editor={createPlateEditor({ id: 'secondary', primary: false })}>
<PlateContent />
</Plate>
</main>
);
});
When a hook called inside a PlateController fails to locate a matching Plate Store, it will use Plate Store's default values. The default value for editor is createPlateFallbackEditor(). A fallback editor works like an empty Plate editor with no plugins, except it throws a runtime error if it receives a Slate operation (i.e. it is immutable and must not be used in transforms).
The rationale behind this is to ensure that code that queries the editor (such as determining whether toolbar buttons are active) fails silently with a sensible default value, while code that transforms the editor (such as pressing a toolbar button) fails loudly with an error.
There are two ways to determine if you're working with a fallback editor or a real editor:
useEditorMounted returns false if no mounted editor could be resolvededitor.meta.isFallback is true for fallback editorsWhen using hooks like useEditorRef inside a PlateController, you should code defensively to ensure that fallback editors are handled appropriately should they arise. For example, you can disable toolbar buttons if useEditorMounted returns false, or ignore events if editor.meta.isFallback is true.
import { KEYS } from 'platejs';
const App = withHoc(PlateController, () => {
const activeEditor = useEditorRef();
const toggleBold = () => {
if (activeEditor.meta.isFallback) return;
activeEditor.tf.toggleMark(KEYS.bold);
};
return (
<main>
<button type="button" onClick={toggleBold}>
Bold
</button>
<Plate editor={createPlateEditor({ id: 'main', primary: false })}>
<PlateContent />
</Plate>
<Plate editor={createPlateEditor({ id: 'secondary', primary: false })}>
<PlateContent />
</Plate>
</main>
);
});
const App = withHoc(PlateController, () => {
const activeEditor = useEditorRef();
const isFallback = !useEditorMounted();
const toggleBold = () => {
activeEditor.tf.toggleMark(KEYS.bold);
};
return (
<main>
<button
type="button"
onClick={toggleBold}
disabled={isFallback}
>
Bold
</button>
<Plate editor={createPlateEditor({ id: 'main', primary: false })}>
<PlateContent />
</Plate>
<Plate editor={createPlateEditor({ id: 'secondary', primary: false })}>
<PlateContent />
</Plate>
</main>
);
});