DEV Community

Ramu Narasinga
Ramu Narasinga

Posted on

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?)
Enter fullscreen mode Exit fullscreen mode

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)}
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

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 }),
 * })
 *

Enter fullscreen mode Exit fullscreen mode

*/
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)
Enter fullscreen mode Exit fullscreen mode

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

References:

  1. GitHub Search: useSyncExternalStore in tiptap
  2. EditorContent.tsx
  3. useEditorState.ts
  4. useEditor.ts
  5. React Docs: useSyncExternalStore

Top comments (0)