1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-11 17:53:59 +02:00

Change how Slate context updates and introduce useSlateSelection hook (#5041)

* Fix DOM selection sync when there are unexpected rerenders

* Create a useSlateSelection hook and expose it

* update docs

* add changeset

* Undo the useEffect change and add a useSlateValue method

* Use a version counter instead for SlateContext

* comment out layout effect prevention for now

* Undo useV comparison for now

* Change the changeset

* Fix lint

* Remove the useSlateValue hook

* remove some unused imports

* Add useSlateWithV to the docs

* fix changeset lint

* Change changeset to minor instead
This commit is contained in:
Bryan Haakman
2022-07-23 01:27:26 +02:00
committed by GitHub
parent 1cc0797f53
commit 9bc0b6132a
7 changed files with 64 additions and 8 deletions

View File

@@ -150,7 +150,7 @@ export const Editable = (props: EditableProps) => {
[]
)
// Whenever the editor updates...
// Whenever the editor updates, sync the DOM selection with the slate selection
useIsomorphicLayoutEffect(() => {
// Update element-related weak maps with the DOM element ref.
let window

View File

@@ -3,7 +3,7 @@ import { Editor, Node, Descendant, Scrubber } from 'slate'
import { ReactEditor } from '../plugin/react-editor'
import { FocusedContext } from '../hooks/use-focused'
import { EditorContext } from '../hooks/use-slate-static'
import { SlateContext } from '../hooks/use-slate'
import { SlateContext, SlateContextValue } from '../hooks/use-slate'
import {
getSelectorContext,
SlateSelectorContext,
@@ -26,7 +26,7 @@ export const Slate = (props: {
const { editor, children, onChange, value, ...rest } = props
const unmountRef = useRef(false)
const [context, setContext] = React.useState<[ReactEditor]>(() => {
const [context, setContext] = React.useState<SlateContextValue>(() => {
if (!Node.isNodeList(value)) {
throw new Error(
`[Slate] value is invalid! Expected a list of elements` +
@@ -41,7 +41,7 @@ export const Slate = (props: {
}
editor.children = value
Object.assign(editor, rest)
return [editor]
return { v: 0, editor }
})
const {
@@ -54,7 +54,10 @@ export const Slate = (props: {
onChange(editor.children)
}
setContext([editor])
setContext(prevContext => ({
v: prevContext.v + 1,
editor,
}))
handleSelectorChange(editor)
}, [onChange])

View File

@@ -0,0 +1,17 @@
import { BaseSelection, Range } from 'slate'
import { useSlateSelector } from './use-slate-selector'
/**
* Get the current slate selection.
* Only triggers a rerender when the selection actually changes
*/
export const useSlateSelection = () => {
return useSlateSelector(editor => editor.selection, isSelectionEqual)
}
const isSelectionEqual = (a: BaseSelection, b: BaseSelection) => {
if (!a && !b) return true
if (!a || !b) return false
return Range.equals(a, b)
}

View File

@@ -7,7 +7,15 @@ import { ReactEditor } from '../plugin/react-editor'
* context whenever changes occur.
*/
export const SlateContext = createContext<[ReactEditor] | null>(null)
export interface SlateContextValue {
v: number
editor: ReactEditor
}
export const SlateContext = createContext<{
v: number
editor: ReactEditor
} | null>(null)
/**
* Get the current editor object from the React context.
@@ -22,6 +30,18 @@ export const useSlate = (): Editor => {
)
}
const [editor] = context
const { editor } = context
return editor
}
export const useSlateWithV = () => {
const context = useContext(SlateContext)
if (!context) {
throw new Error(
`The \`useSlate\` hook must be used inside the <Slate> component's context.`
)
}
return context
}

View File

@@ -23,8 +23,9 @@ export { useSlateStatic } from './hooks/use-slate-static'
export { useFocused } from './hooks/use-focused'
export { useReadOnly } from './hooks/use-read-only'
export { useSelected } from './hooks/use-selected'
export { useSlate } from './hooks/use-slate'
export { useSlate, useSlateWithV } from './hooks/use-slate'
export { useSlateSelector } from './hooks/use-slate-selector'
export { useSlateSelection } from './hooks/use-slate-selection'
// Plugin
export { ReactEditor } from './plugin/react-editor'