Blog
useSyncExternalStore usage in TipTap source code

useSyncExternalStore usage in TipTap source code

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<TSelectorResult>(
  options: UseEditorStateOptions<TSelectorResult, Editor> | UseEditorStateOptions<TSelectorResult, Editor | null>,
): 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<TSelectorResult, Editor | null>['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, import is from use-sync-external-store/shim/with-selector.

useEditor.ts

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, videos.

I am open to work 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

References:

  1. useSyncExternalStore&type=code

  2. packages/react/src/EditorContent.tsx#L32

  3. packages/react/src/useEditorState.ts#L162

  4. packages/react/src/useEditor.ts#L345

  5. https://react.dev/reference/react/useSyncExternalStore