Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make onChange prop optional, update examples and docs to treat slate as uncontrolled #4922

Merged
merged 2 commits into from
Apr 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/red-bottles-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'slate-react': patch
---

Make Slate component onChange optional
70 changes: 18 additions & 52 deletions docs/walkthroughs/01-installing-slate.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,53 +66,23 @@ declare module 'slate' {
}
```

```typescript
// Also you must annotate `useState<Descendant[]>` and the editor's initial value.
const App = () => {
const initialValue: CustomElement[] = []
const [value, setValue] = useState<Descendant[]>(initialValue)
return (
<Slate value={value} onChange={setValue}>
...
</Slate>
)
}
```

Next we want to create state for `value`:

```jsx
const App = () => {
const [editor] = useState(() => withReact(createEditor()))

// Keep track of state for the value of the editor.
const [value, setValue] = useState([])
return null
}
```

Next up is to render a `<Slate>` context provider.

The provider component keeps track of your Slate editor, its plugins, its value, its selection, and any changes that occur. It **must** be rendered above any `<Editable>` components. But it can also provide the editor state to other components like toolbars, menus, etc. using the `useSlate` hook.

```jsx
const initialValue = []

const App = () => {
const [editor] = useState(() => withReact(createEditor()))
const [value, setValue] = useState([])
// Render the Slate context.
return (
<Slate
editor={editor}
value={value}
onChange={newValue => setValue(newValue)}
/>
)
return <Slate editor={editor} value={initialValue} />
}
```

You can think of the `<Slate>` component as providing a context to every component underneath it.

> As of v0.67 the Slate Provider's "value" prop is now only used as initial state for editor.children. If your code relies on replacing editor.children you should do so by replacing it directly instead of relying on the "value" prop to do this for you. See [Slate PR 4540](https://github.com/ianstormtaylor/slate/pull/4540) for a more in-depth discussion.
> Slate Provider's "value" prop is only used as initial state for editor.children. If your code relies on replacing editor.children you should do so by replacing it directly instead of relying on the "value" prop to do this for you. See [Slate PR 4540](https://github.com/ianstormtaylor/slate/pull/4540) for a more in-depth discussion.

This is a slightly different mental model than things like `<input>` or `<textarea>`, because richtext documents are more complex. You'll often want to include toolbars, or live previews, or other complex components next to your editable content.

Expand All @@ -121,16 +91,14 @@ By having a shared context, those other components can execute commands, query t
Okay, so the next step is to render the `<Editable>` component itself:

```jsx
const initialValue = []

const App = () => {
const [editor] = useState(() => withReact(createEditor()))
const [value, setValue] = useState([])
return (
// Add the editable component inside the context.
<Slate
editor={editor}
value={value}
onChange={newValue => setValue(newValue)}
>
<Slate editor={editor} value={initialValue}>
<Editable />
</Slate>
)
Expand All @@ -144,22 +112,20 @@ There's only one last step. So far we've been using an empty `[]` array as the i
The value is just plain JSON. Here's one containing a single paragraph block with some text in it:

```jsx
// Add the initial value.
const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const [editor] = useState(() => withReact(createEditor()))
// Add the initial value when setting up our state.
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
])
const [editor] = useState(() => withReact(createEditor())
const [value, setValue] = useState()

return (
<Slate
editor={editor}
value={value}
onChange={newValue => setValue(newValue)}
>
<Slate editor={editor} value={initialValue}>
<Editable />
</Slate>
)
Expand Down
45 changes: 24 additions & 21 deletions docs/walkthroughs/02-adding-event-handlers.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ Let's use the `onKeyDown` handler to change the editor's content when we press a
Here's our app from earlier:

```jsx
const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const editor = useMemo(() => withReact(createEditor()), [])
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
])

return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Slate editor={editor} value={initialValue}>
<Editable />
</Slate>
)
Expand All @@ -29,17 +30,18 @@ const App = () => {
Now we add an `onKeyDown` handler:

```jsx
const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const editor = useMemo(() => withReact(createEditor()), [])
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
])

return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Slate editor={editor} value={initialValue}>
<Editable
// Define a new handler which prints the key that was pressed.
onKeyDown={event => {
Expand All @@ -58,17 +60,18 @@ Now we want to make it actually change the content. For the purposes of our exam
Our `onKeyDown` handler might look like this:

```jsx
const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const editor = useMemo(() => withReact(createEditor()), [])
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
])

return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Slate editor={editor} value={initialValue}>
<Editable
onKeyDown={event => {
if (event.key === '&') {
Expand Down
60 changes: 32 additions & 28 deletions docs/walkthroughs/03-defining-custom-elements.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ But that's not all you can do. Slate lets you define any type of custom blocks y
We'll show you how. Let's start with our app from earlier:

```jsx
const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const editor = useMemo(() => withReact(createEditor()), [])
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
])

return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Slate editor={editor} value={initialValue}>
<Editable
onKeyDown={event => {
if (event.key === '&') {
Expand Down Expand Up @@ -65,14 +66,15 @@ const DefaultElement = props => {
Now, let's add that renderer to our `Editor`:

```jsx
const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const editor = useMemo(() => withReact(createEditor()), [])
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
])

// Define a rendering function based on the element passed to `props`. We use
// `useCallback` here to memoize the function for subsequent renders.
Expand All @@ -86,7 +88,7 @@ const App = () => {
}, [])

return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Slate editor={editor} value={initialValue}>
<Editable
// Pass in the `renderElement` function.
renderElement={renderElement}
Expand Down Expand Up @@ -120,14 +122,15 @@ Okay, but now we'll need a way for the user to actually turn a block into a code
// Import the `Editor` and `Transforms` helpers from Slate.
import { Editor, Transforms } from 'slate'

const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const editor = useMemo(() => withReact(createEditor()), [])
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
])

const renderElement = useCallback(props => {
switch (props.element.type) {
Expand All @@ -139,7 +142,7 @@ const App = () => {
}, [])

return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Slate editor={editor} value={initialValue}>
<Editable
renderElement={renderElement}
onKeyDown={event => {
Expand Down Expand Up @@ -177,14 +180,15 @@ Now, if you press ``Ctrl-``` the block your cursor is in should turn into a code
But we forgot one thing. When you hit ``Ctrl-``` again, it should change the code block back into a paragraph. To do that, we'll need to add a bit of logic to change the type we set based on whether any of the currently selected blocks are already a code block:

```jsx
const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const editor = useMemo(() => withReact(createEditor()), [])
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
])

const renderElement = useCallback(props => {
switch (props.element.type) {
Expand All @@ -196,7 +200,7 @@ const App = () => {
}, [])

return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Slate editor={editor} value={initialValue}>
<Editable
renderElement={renderElement}
onKeyDown={event => {
Expand Down
Loading