ianstormtaylor / slate

A completely customizable framework for building rich text editors. (Currently in beta.)
http://slatejs.org
MIT License
29.71k stars 3.24k forks source link

provide example of a controlled slate component #1627

Closed bogdansoare closed 6 years ago

bogdansoare commented 6 years ago

Could you please provide an example on how to create a controlled slate component which gets its value from the props rather than the state? I've tried it but on each change the editor was losing focus

conorcussell commented 6 years ago

Can you provide a JSFiddle with your current code?

A working example can be seen here https://jsfiddle.net/conorcussell/g5q576Lq/1/ but feel free to jump in the slack channel and ask any questions

bogdansoare commented 6 years ago

Here is a codesanbox https://codesandbox.io/s/9367264y3w

conorcussell commented 6 years ago

@bogdansoare here is a fixed version https://codesandbox.io/s/3kx5pxv7om

There were two issues:

  1. Because you are serializing and deserializing the value to html, you are losing the selection https://docs.slatejs.org/slate-core/value#selection

old

<RichTextEditor
  value={htmlSerializer.deserialize(this.state.value)}
  onChange={value => this.setState({ value: htmlSerializer.serialize(value) })}
/>

new

state = {
    value: htmlSerializer.deserialize("<p>Lorem <strong>ipsum</strong></p>")
};
...
<RichTextEditor
  value={this.state.value}
  onChange={value => this.setState({ value })}
/>
  1. Because you are only updating the parent component's state when the document has changed, the current selection is not being updated.

old

handleChange = ({ value }) => {
    const { onChange } = this.props;

    if (onChange) {
      if (value.document !== this.props.value.document) {
        onChange(value);
      }
    }
};

new

handleChange = ({ value }) => {
    const { onChange } = this.props;

    onChange(value);
};

Let me know if that makes sense?

bogdansoare commented 6 years ago

@conorcussell thank very much for the explanation. The issue is that I want to store the value as Html instead of the internal state of slate. Any ideas on how to do that?

conorcussell commented 6 years ago

@bogdansoare here is a new fork to show you how https://codesandbox.io/s/ovrkx9o0z5.

I added a method to the parent component to handle changes, it conditionally serializes the value to html if the document has changed and updates the state. The state now stores both the full Slate value and a string respresentation of the html.

handleChange = value => {
    let newState = {
      ...this.state,
      value
    };

    if (value.document !== this.state.value.document) {
      // handle a document change
      newState = {
        ...newState,
        html: htmlSerializer.serialize(value)
      };
    }

    this.setState(newState);
};
...
<RichTextEditor
  value={this.state.value}
  onChange={this.handleChange}
/>
conorcussell commented 6 years ago

Closing this now in favour of Slack feel free to DM me if you have any more questions, otherwise questions on the general channel usually get answered quickly.