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

Code before:
  const [, forceRender] = useReducer(s => s + 1, 0)
  EDITOR_TO_FORCE_RENDER.set(editor, forceRender)

  // Update internal state on each render.
  IS_READ_ONLY.set(editor, readOnly)

  // Keep track of some state for the event handler logic.
  const state = useMemo(
    () => ({
      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)

Review comment (suggestion):
Typing this as `AndroidInputManager | null | undefined` will fix the "used before defined" issue.

Code after:
  const [, forceRender] = useReducer(s => s + 1, 0)
  EDITOR_TO_FORCE_RENDER.set(editor, forceRender)

  // Update internal state on each render.
  IS_READ_ONLY.set(editor, readOnly)

  // Keep track of some state for the event handler logic.
  const state = useMemo(
    () => ({
      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