mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-23 07:22:55 +02:00
Add error boundary to save Slate from a crash on corrupt DOM (#2792)
* Working version of restore dom * Fix linting errors * Add button to corrupt DOM * Added error boundary that fixes DOM on render error * Fix linting errors * Fix debug output for componentDidCatch * Improve example by adding a separate restoreDOM button * Remove key change from error boundary which is not necessary * Fix linting error
This commit is contained in:
@@ -3,7 +3,7 @@ import { Value } from 'slate'
|
||||
|
||||
import React from 'react'
|
||||
import initialValue from './value.json'
|
||||
import { Button, Icon, Toolbar } from '../components'
|
||||
import { Button, EditorValue, Icon, Instruction, Toolbar } from '../components'
|
||||
|
||||
/**
|
||||
* The Restore DOM example.
|
||||
@@ -31,7 +31,7 @@ class RestoreDOMExample extends React.Component {
|
||||
|
||||
state = {
|
||||
value: Value.fromJSON(initialValue),
|
||||
bgcolor: '#ffffff',
|
||||
bgcolor: '#ffeecc',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,12 +53,25 @@ class RestoreDOMExample extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Instruction>
|
||||
<ol>
|
||||
<li>
|
||||
Click a brush to change color in state. Use refresh button to
|
||||
`restoreDOM` which renders changes.
|
||||
</li>
|
||||
<li>
|
||||
Press `!` button to corrupt DOM by removing `bold`. Backspace from
|
||||
start of `text` 5 times. Console will show error but Slate will
|
||||
recover by restoring DOM.
|
||||
</li>
|
||||
</ol>
|
||||
</Instruction>
|
||||
<Toolbar>
|
||||
{this.renderHighlightButton('#ffffff')}
|
||||
{this.renderHighlightButton('#ffeecc')}
|
||||
{this.renderHighlightButton('#ffffcc')}
|
||||
{this.renderHighlightButton('#ccffcc')}
|
||||
{this.renderHighlightButton('#ccffff')}
|
||||
{this.renderCorruptButton()}
|
||||
{this.renderRestoreButton()}
|
||||
</Toolbar>
|
||||
<Editor
|
||||
spellCheck
|
||||
@@ -68,7 +81,9 @@ class RestoreDOMExample extends React.Component {
|
||||
value={this.state.value}
|
||||
onChange={this.onChange}
|
||||
renderBlock={this.renderBlock}
|
||||
renderMark={this.renderMark}
|
||||
/>
|
||||
<EditorValue value={this.state.value} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -93,6 +108,48 @@ class RestoreDOMExample extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render restoreDOM button
|
||||
*/
|
||||
|
||||
renderRestoreButton = () => {
|
||||
const { editor } = this
|
||||
|
||||
function restoreDOM() {
|
||||
editor.restoreDOM()
|
||||
}
|
||||
|
||||
return (
|
||||
<Button onMouseDown={restoreDOM}>
|
||||
<Icon>refresh</Icon>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a button to corrupt the DOM
|
||||
*
|
||||
*@return {Element}
|
||||
*/
|
||||
|
||||
renderCorruptButton = () => {
|
||||
/**
|
||||
* Corrupt the DOM by forcibly deleting the first instance we can find
|
||||
* of the `bold` text in the DOM.
|
||||
*/
|
||||
|
||||
function corrupt() {
|
||||
const boldEl = window.document.querySelector('[data-bold]')
|
||||
const el = boldEl.closest('[data-slate-object="text"]')
|
||||
el.parentNode.removeChild(el)
|
||||
}
|
||||
return (
|
||||
<Button onMouseDown={corrupt}>
|
||||
<Icon>error_outline</Icon>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight every block with a given background color
|
||||
*
|
||||
@@ -100,9 +157,7 @@ class RestoreDOMExample extends React.Component {
|
||||
*/
|
||||
|
||||
onClickHighlight = bgcolor => {
|
||||
const { editor } = this
|
||||
this.setState({ bgcolor })
|
||||
editor.restoreDOM()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,6 +183,29 @@ class RestoreDOMExample extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a Slate mark.
|
||||
*
|
||||
* @param {Object} props
|
||||
* @return {Element}
|
||||
*/
|
||||
|
||||
renderMark = (props, editor, next) => {
|
||||
const { children, mark, attributes } = props
|
||||
|
||||
switch (mark.type) {
|
||||
case 'bold':
|
||||
// Added `data-bold` so we can find bold text with `querySelector`
|
||||
return (
|
||||
<strong {...attributes} data-bold>
|
||||
{children}
|
||||
</strong>
|
||||
)
|
||||
default:
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On change, save the new `value`.
|
||||
*
|
||||
|
@@ -9,7 +9,16 @@
|
||||
"nodes": [
|
||||
{
|
||||
"object": "text",
|
||||
"text": "First block of text"
|
||||
"text": "First block with "
|
||||
},
|
||||
{
|
||||
"object": "text",
|
||||
"text": "bold",
|
||||
"marks": [{ "type": "bold" }]
|
||||
},
|
||||
{
|
||||
"object": "text",
|
||||
"text": " text in it"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -19,7 +28,16 @@
|
||||
"nodes": [
|
||||
{
|
||||
"object": "text",
|
||||
"text": "Second block of text"
|
||||
"text": "Second block with "
|
||||
},
|
||||
{
|
||||
"object": "text",
|
||||
"text": "bold",
|
||||
"marks": [{ "type": "bold" }]
|
||||
},
|
||||
{
|
||||
"object": "text",
|
||||
"text": " text in it"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -29,7 +47,16 @@
|
||||
"nodes": [
|
||||
{
|
||||
"object": "text",
|
||||
"text": "Third block of text"
|
||||
"text": "Third block with "
|
||||
},
|
||||
{
|
||||
"object": "text",
|
||||
"text": "bold",
|
||||
"marks": [{ "type": "bold" }]
|
||||
},
|
||||
{
|
||||
"object": "text",
|
||||
"text": " text in it"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -75,6 +75,22 @@ class Content extends React.Component {
|
||||
tagName: 'div',
|
||||
}
|
||||
|
||||
/**
|
||||
* An error boundary. If there is a render error, we increment `errorKey`
|
||||
* which is part of the container `key` which forces a re-render from
|
||||
* scratch.
|
||||
*
|
||||
* @param {Error} error
|
||||
* @param {String} info
|
||||
*/
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
debug('componentDidCatch', { error, info })
|
||||
// The call to `setState` is required despite not setting a value.
|
||||
// Without this call, React will not try to recreate the component tree.
|
||||
this.setState({})
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary values.
|
||||
*
|
||||
|
Reference in New Issue
Block a user