In this article, we will review useSyncExternalStore usage in TipTap.
I found useSyncExternalStore
is used in 3 files:
Before we look at these files, let’s learn what is useSyncExternalStore
.
useSyncExternalStore
useSyncExternalStore
is a React Hook that lets you subscribe to an external store.
Read more about useSyncExternalStore.
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
EditorContent.tsx
import { useSyncExternalStore } from 'use-sync-external-store/shim'
...
/**
* This component renders all of the editor's node views.
*/
const Portals: React.FC<{ contentComponent: ContentComponent }> = ({
contentComponent,
}) => {
// For performance reasons, we render the node view portals on state changes only
const renderers = useSyncExternalStore(
contentComponent.subscribe,
contentComponent.getSnapshot,
contentComponent.getServerSnapshot,
)
// This allows us to directly render the portals without any additional wrapper
return (
<>
{Object.values(renderers)}
</>
)
}
Pay attention to the comment in this above code snippet:
For performance reasons, we render the node view portals on state changes only
useEditorState.ts
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
...
/**
* This hook allows you to watch for changes on the editor instance.
* It will allow you to select a part of the editor state and re-render the component when it changes.
* @example
* ```
tsx
* const editor = useEditor({...options})
* const { currentSelection } = useEditorState({
* editor,
* selector: snapshot => ({ currentSelection: snapshot.editor.state.selection }),
* })
*
*/
export function useEditorState(
options: UseEditorStateOptions | UseEditorStateOptions,
): TSelectorResult | null {
const [editorStateManager] = useState(() => new EditorStateManager(options.editor))
// Using the useSyncExternalStore
hook to sync the editor instance with the component state
const selectedState = useSyncExternalStoreWithSelector(
editorStateManager.subscribe,
editorStateManager.getSnapshot,
editorStateManager.getServerSnapshot,
options.selector as UseEditorStateOptions['selector'],
options.equalityFn ?? deepEqual,
)
useIsomorphicLayoutEffect(() => {
return editorStateManager.watch(options.editor)
}, [options.editor, editorStateManager])
useDebugValue(selectedState)
return selectedState
}
> *Using the* `useSyncExternalStore` *hook to sync the editor instance with the component state*
In the `EditorContent.tsx`, the import was from `use-sync-external-store/shim` but here in this `useEditorState`, the import is from `use-sync-external-store/shim/with-selector`.
# **useEditor.ts**
```javascript
import { useSyncExternalStore } from 'use-sync-external-store/shim'
...
export function useEditor(
options: UseEditorOptions = {},
deps: DependencyList = [],
): Editor | null {
const mostRecentOptions = useRef(options)
mostRecentOptions.current = options
const [instanceManager] = useState(() => new EditorInstanceManager(mostRecentOptions))
const editor = useSyncExternalStore(
instanceManager.subscribe,
instanceManager.getEditor,
instanceManager.getServerSnapshot,
)
useDebugValue(editor)
Here the external store is instanceManager
.
About me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles and videos.
I am open to working on interesting projects. Send me an email at ramu.narasinga@gmail.com
- My Github — https://github.com/ramu-narasinga
- My website — https://ramunarasinga.com
- My Youtube channel — https://www.youtube.com/@thinkthroo
- Learning platform — https://thinkthroo.com
- Codebase Architecture — https://app.thinkthroo.com/architecture
- Best practices — https://app.thinkthroo.com/best-practices
- Production-grade projects — https://app.thinkthroo.com/production-grade-projects
Top comments (0)