DEV Community

Ramu Narasinga
Ramu Narasinga

Posted on • Edited on • Originally published at thinkthroo.com

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

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Postgres on Neon - Get the Free Plan

No credit card required. The database you love, on a serverless platform designed to help you build faster.

Get Postgres on Neon