Repository: ianstormtaylor/slate
File: packages/slate-react/src/components/editable.tsx (TypeScript)

Code before:
    () => ({
      isDraggingInternally: false,
      isUpdatingSelection: false,
      latestElement: null as DOMElement | null,
      hasMarkPlaceholder: false,
    }),
    []
  )

  // The autoFocus TextareaHTMLAttribute doesn't do anything on a div, so it
  // needs to be manually focused.
  useEffect(() => {
    if (ref.current && autoFocus) {
      ref.current.focus()
    }
  }, [autoFocus])

  let androidInputManager: AndroidInputManager | null

  // Listen on the native `selectionchange` event to be able to update any time
  // the selection changes. This is required because React's `onSelect` is leaky
  // and non-standard so it doesn't fire until after a selection has been
  // released. This causes issues in situations where another change happens
  // while a selection is being dragged.
  // eslint chokes on throttle and androidInputManager! so disabling
  // eslint-disable-next-line react-hooks/exhaustive-deps
  const onDOMSelectionChange = useCallback(
    throttle(() => {
      if (
        (IS_ANDROID || !ReactEditor.isComposing(editor)) &&
        (!state.isUpdatingSelection || androidInputManager?.isFlushing()) &&
        !state.isDraggingInternally
      ) {
        const root = ReactEditor.findDocumentOrShadowRoot(editor)
        const { activeElement } = root
        const el = ReactEditor.toDOMNode(editor, editor)
        const domSelection = root.getSelection()

        if (activeElement === el) {
          state.latestElement = activeElement
          IS_FOCUSED.set(editor, true)
        } else {
          IS_FOCUSED.delete(editor)
        }

        if (!domSelection) {
          return Transforms.deselect(editor)
        }

        const { anchorNode, focusNode } = domSelection


Review comment (suggestion):
You should be able to get around this issue and still keep the dependency check if you replace the `useCallback` with a `useMemo`, like

```ts
const onDOMSelectionChange = useMemo(
  () => throttle(() => { ... }),
  [androidInputManager, editor, readOnly, state]
)
```

Code after:
    () => ({
      isDraggingInternally: false,
      isUpdatingSelection: false,
      latestElement: null as DOMElement | null,
      hasMarkPlaceholder: false,
    }),
    []
  )

  // The autoFocus TextareaHTMLAttribute doesn't do anything on a div, so it
  // needs to be manually focused.
  useEffect(() => {
    if (ref.current && autoFocus) {
      ref.current.focus()
    }
  }, [autoFocus])

  /**
   * The AndroidInputManager object has a cyclical dependency on onDOMSelectionChange
   *
   * It is defined as a reference to simplify hook dependencies and clarify that
   * it needs to be initialized.
   */
  const androidInputManagerRef = useRef<
    AndroidInputManager | null | undefined
  >()

  // Listen on the native `selectionchange` event to be able to update any time
  // the selection changes. This is required because React's `onSelect` is leaky
  // and non-standard so it doesn't fire until after a selection has been
  // released. This causes issues in situations where another change happens
  // while a selection is being dragged.
  const onDOMSelectionChange = useMemo(
    () =>
      throttle(() => {
        const androidInputManager = androidInputManagerRef.current
        if (
          (IS_ANDROID || !ReactEditor.isComposing(editor)) &&
          (!state.isUpdatingSelection || androidInputManager?.isFlushing()) &&
          !state.isDraggingInternally
        ) {
          const root = ReactEditor.findDocumentOrShadowRoot(editor)
          const { activeElement } = root
          const el = ReactEditor.toDOMNode(editor, editor)
          const domSelection = root.getSelection()

          if (activeElement === el) {
            state.latestElement = activeElement
            IS_FOCUSED.set(editor, true)
          } else {
            IS_FOCUSED.delete(editor)