mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-19 13:41:19 +02:00
update walkthroughs
This commit is contained in:
@@ -16,8 +16,6 @@ _Note, if you'd rather use a pre-bundled version of Slate, you can `yarn add sla
|
||||
|
||||
Once you've installed Slate, you'll need to import it.
|
||||
|
||||
Slate exposes a set of modules that you'll use to build your editor. The most important of which are the `Editor` class and the `<Editable>` component.
|
||||
|
||||
```js
|
||||
// Import the Slate editor factory.
|
||||
import { createEditor } from 'slate'
|
||||
@@ -47,19 +45,44 @@ const App = () => {
|
||||
|
||||
Of course we haven't rendered anything, so you won't see any changes.
|
||||
|
||||
Next up is to render a `<Slate>` context provider.
|
||||
|
||||
The provider component keeps track of your Slate editor, its plugins, its default value, 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.
|
||||
Next we want to create state for `value` and `selection`:
|
||||
|
||||
```jsx
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
// Render the Slate editor context.
|
||||
return <Slate editor={editor} />
|
||||
|
||||
// Keep track of state for the value and selection of the editor.
|
||||
const [value, setValue] = useState([])
|
||||
const [selection, setSelection] = useState(null)
|
||||
return null
|
||||
}
|
||||
```
|
||||
|
||||
You can think of the `<Slate>` component as provided an "un-controlled" editor context to every component underneath it.
|
||||
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 App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [value, setValue] = useState([])
|
||||
const [selection, setSelection] = useState(null)
|
||||
// Render the Slate context.
|
||||
return (
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
You can think of the `<Slate>` component as providing a "controlled" context to every component underneath it.
|
||||
|
||||
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.
|
||||
|
||||
@@ -70,9 +93,19 @@ Okay, so the next step is to render the `<Editable>` component itself:
|
||||
```jsx
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [value, setValue] = useState([])
|
||||
const [selection, setSelection] = useState(null)
|
||||
return (
|
||||
// Add the editable component inside the context.
|
||||
<Slate editor={editor}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable />
|
||||
</Slate>
|
||||
)
|
||||
@@ -81,28 +114,32 @@ const App = () => {
|
||||
|
||||
The `<Editable>` component acts like `contenteditable`. Anywhere you render it will render an editable richtext document for the nearest editor context.
|
||||
|
||||
There's only one last step. So far we haven't defined what the default value of the editor is, so it's empty. Let's fix that by defining an initial value.
|
||||
There's only one last step. So far we've been using an empty `[]` array as the initial value of the editor, so it has no content. Let's fix that by defining an initial value.
|
||||
|
||||
The value is just plain JSON. Here's one containing a single paragraph block with some text in it:
|
||||
|
||||
```js
|
||||
// Create our default value...
|
||||
const defaultValue = [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
text: 'A line of text in a paragraph.',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
// Add the initial value when setting up our state.
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
// Add the default value as a prop to the editor context.
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable />
|
||||
</Slate>
|
||||
)
|
||||
@@ -112,5 +149,3 @@ const App = () => {
|
||||
There you have it!
|
||||
|
||||
That's the most basic example of Slate. If you render that onto the page, you should see a paragraph with the text `A line of text in a paragraph.`. And when you type, you should see the text change!
|
||||
|
||||
You'll notice that there is no `onChange` handler defined. That's because the `<Slate>` context acts like an **un-controlled** component, with the changes automatically being propagated to any context consumers. However, just like with un-controlled components you can attach an `onChange` prop to listen for changes. We'll cover that later.
|
||||
|
@@ -11,8 +11,24 @@ Here's our app from earlier:
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable />
|
||||
</Slate>
|
||||
)
|
||||
@@ -24,8 +40,24 @@ Now we add an `onKeyDown` handler:
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
// Define a new handler which prints the key that was pressed.
|
||||
onKeyDown={event => {
|
||||
@@ -46,8 +78,24 @@ Our `onKeyDown` handler might look like this:
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
onKeyDown={event => {
|
||||
if (event.key === '&') {
|
||||
|
@@ -9,8 +9,24 @@ We'll show you how. Let's start with our app from earlier:
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
onKeyDown={event => {
|
||||
if (event.key === '&') {
|
||||
@@ -60,6 +76,13 @@ Now, let's add that renderer to our `Editor`:
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
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.
|
||||
@@ -73,7 +96,15 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
// Pass in the `renderElement` function.
|
||||
renderElement={renderElement}
|
||||
@@ -109,6 +140,14 @@ import { Editor } from 'slate'
|
||||
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (props.element.type) {
|
||||
case 'code':
|
||||
@@ -119,7 +158,15 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
renderElement={renderElement}
|
||||
onKeyDown={event => {
|
||||
@@ -154,8 +201,15 @@ But we forgot one thing. When you hit `` Ctrl-` `` again, it should change the c
|
||||
|
||||
```js
|
||||
const App = () => {
|
||||
const [value, setValue] = useState(initialValue)
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (props.element.type) {
|
||||
case 'code':
|
||||
@@ -166,20 +220,26 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
renderElement={renderElement}
|
||||
onKeyDown={event => {
|
||||
if (event.key === '`' && event.ctrlKey) {
|
||||
event.preventDefault()
|
||||
// Determine whether any of the currently selected blocks are code blocks.
|
||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
const isCodeActive = !!node
|
||||
|
||||
// Toggle the block type depending on `isCode`.
|
||||
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
// Toggle the block type depending on whether there's already a match.
|
||||
Editor.setNodes(
|
||||
editor,
|
||||
{ type: isCodeActive ? 'paragraph' : 'code' },
|
||||
{ type: match ? 'paragraph' : 'code' },
|
||||
{ match: 'block' }
|
||||
)
|
||||
}
|
||||
|
@@ -9,6 +9,14 @@ So we start with our app from earlier:
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (props.element.type) {
|
||||
case 'code':
|
||||
@@ -19,18 +27,25 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
renderElement={renderElement}
|
||||
onKeyDown={event => {
|
||||
if (event.key === '`' && event.ctrlKey) {
|
||||
event.preventDefault()
|
||||
const { selection } = editor
|
||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
const isCodeActive = !!node
|
||||
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
Editor.setNodes(
|
||||
editor,
|
||||
{ type: isCodeActive ? 'paragraph' : 'code' },
|
||||
{ type: match ? 'paragraph' : 'code' },
|
||||
{ match: 'block' }
|
||||
)
|
||||
}
|
||||
@@ -46,6 +61,14 @@ And now, we'll edit the `onKeyDown` handler to make it so that when you press `c
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (prop.element.type) {
|
||||
case 'code':
|
||||
@@ -56,7 +79,15 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
renderElement={renderElement}
|
||||
onKeyDown={event => {
|
||||
@@ -68,11 +99,10 @@ const App = () => {
|
||||
// When "`" is pressed, keep our existing code block logic.
|
||||
case '`': {
|
||||
event.preventDefault()
|
||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
const isCodeActive = !!node
|
||||
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
Editor.setNodes(
|
||||
editor,
|
||||
{ type: isCodeActive ? null : 'code' },
|
||||
{ type: match ? 'paragraph' : 'code' },
|
||||
{ match: 'block' }
|
||||
)
|
||||
break
|
||||
@@ -123,6 +153,14 @@ And now, let's tell Slate about that leaf. To do that, we'll pass in the `render
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (props.element.type) {
|
||||
case 'code':
|
||||
@@ -138,7 +176,15 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
renderElement={renderElement}
|
||||
// Pass in the `renderLeaf` function.
|
||||
@@ -151,11 +197,10 @@ const App = () => {
|
||||
switch (event.key) {
|
||||
case '`': {
|
||||
event.preventDefault()
|
||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
const isCodeActive = !!node
|
||||
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
Editor.setNodes(
|
||||
editor,
|
||||
{ type: isCodeActive ? null : 'code' },
|
||||
{ type: match ? null : 'code' },
|
||||
{ match: 'block' }
|
||||
)
|
||||
break
|
||||
|
@@ -13,6 +13,14 @@ We'll start with our app from earlier:
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (props.element.type) {
|
||||
case 'code':
|
||||
@@ -27,7 +35,15 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
renderElement={renderElement}
|
||||
renderLeaf={renderLeaf}
|
||||
@@ -39,11 +55,10 @@ const App = () => {
|
||||
switch (event.key) {
|
||||
case '`': {
|
||||
event.preventDefault()
|
||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
const isCodeActive = !!node
|
||||
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||
Editor.setNodes(
|
||||
editor,
|
||||
{ type: isCodeActive ? null : 'code' },
|
||||
{ type: match ? null : 'code' },
|
||||
{ match: 'block' }
|
||||
)
|
||||
break
|
||||
@@ -79,6 +94,14 @@ const withCustom = editor => {
|
||||
const App = () => {
|
||||
// Wrap the editor with our new `withCustom` plugin.
|
||||
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (props.element.type) {
|
||||
case 'code':
|
||||
@@ -93,7 +116,15 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
renderElement={renderElement}
|
||||
renderLeaf={renderLeaf}
|
||||
@@ -193,6 +224,14 @@ const CustomEditor = {
|
||||
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (props.element.type) {
|
||||
case 'code':
|
||||
@@ -207,16 +246,24 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable
|
||||
renderElement={renderElement}
|
||||
renderLeaf={renderLeaf}
|
||||
// Replace the `onKeyDown` logic with our new commands.
|
||||
onKeyDown={event => {
|
||||
if (!event.ctrlKey) {
|
||||
return
|
||||
}
|
||||
|
||||
// Replace the `onKeyDown` logic with our new commands.
|
||||
switch (event.key) {
|
||||
case '`': {
|
||||
event.preventDefault()
|
||||
@@ -242,6 +289,14 @@ Now our commands are clearly defined and you can invoke them from anywhere we ha
|
||||
```js
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
const renderElement = useCallback(props => {
|
||||
switch (props.element.type) {
|
||||
case 'code':
|
||||
@@ -256,8 +311,16 @@ const App = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
// Add a toolbar with buttons that call the same methods.
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
// Add a toolbar with buttons that call the same methods.
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
onMouseDown={event => {
|
||||
|
@@ -7,24 +7,26 @@ In this guide, we'll show you how to add logic to save your Slate content to a d
|
||||
Let's start with a basic editor:
|
||||
|
||||
```js
|
||||
import React, { useMemo } from 'react'
|
||||
import { createEditor } from 'slate'
|
||||
import { Slate, Editable, withReact } from 'slate-react'
|
||||
|
||||
const defaultValue = [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
text: 'A line of text in a paragraph.',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} defaultValue={defaultValue}>
|
||||
<Slate
|
||||
editor={editor}
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
}}
|
||||
>
|
||||
<Editable />
|
||||
</Slate>
|
||||
)
|
||||
@@ -50,11 +52,23 @@ const defaultValue = [
|
||||
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [value, setValue] = useState([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
<Slate
|
||||
editor={editor}
|
||||
defaultValue={defaultValue}
|
||||
onChange={value => {
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
|
||||
// Save the value to Local Storage.
|
||||
const content = JSON.stringify(value)
|
||||
localStorage.setItem('content', content)
|
||||
@@ -71,25 +85,27 @@ Now whenever you edit the page, if you look in Local Storage, you should see the
|
||||
But... if you refresh the page, everything is still reset. That's because we need to make sure the initial value is pulled from that same Local Storage location, like so:
|
||||
|
||||
```js
|
||||
// Update the initial content to be pulled from Local Storage if it exists.
|
||||
const existingValue = JSON.parse(localStorage.getItem('content'))
|
||||
const defaultValue = existingValue || [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
text: 'A line of text in a paragraph.',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
// Update the initial content to be pulled from Local Storage if it exists.
|
||||
const [value, setValue] = useState(
|
||||
JSON.parse(localStorage.getItem('content')) || [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }],
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<Slate
|
||||
editor={editor}
|
||||
defaultValue={defaultValue}
|
||||
onChange={value => {
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
const content = JSON.stringify(value)
|
||||
localStorage.setItem('content', content)
|
||||
}}
|
||||
@@ -131,20 +147,24 @@ const deserialize = string => {
|
||||
})
|
||||
}
|
||||
|
||||
// Use our deserializing function to read the data from Local Storage.
|
||||
const existingValue = localStorage.getItem('content')
|
||||
const initialValue = deserialize(existingValue || '')
|
||||
|
||||
const App = () => {
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const [selection, setSelection] = useState(null)
|
||||
// Use our deserializing function to read the data from Local Storage.
|
||||
const [value, setValue] = useState(
|
||||
deserialize(localStorage.getItem('content')) || ''
|
||||
)
|
||||
|
||||
return (
|
||||
<Slate
|
||||
editor={editor}
|
||||
defaultValue={defaultValue}
|
||||
onChange={value => {
|
||||
value={value}
|
||||
selection={selection}
|
||||
onChange={(value, selection) => {
|
||||
setValue(value)
|
||||
setSelection(selection)
|
||||
// Serialize the value and save the string value to Local Storage.
|
||||
const content = serialize(value)
|
||||
localStorage.setItem('content', content)
|
||||
localStorage.setItem('content', serialize(value))
|
||||
}}
|
||||
>
|
||||
<Editable />
|
||||
|
Reference in New Issue
Block a user