1
0
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:
Sunny Hirai
2019-05-16 16:26:13 -07:00
committed by GitHub
parent fd8e18b9cf
commit 48eb6700b7
3 changed files with 130 additions and 9 deletions

View File

@@ -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`.
*

View File

@@ -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"
}
]
}

View File

@@ -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.
*