React富文本编辑器开发(六)

本文介绍了如何在Slate组件中实现内容的本地数据持久化,包括使用onChange事件监听内容变化并将数据保存到localStorage。同时探讨了自定义序列化和反序列化功能,如纯文本格式和HTML/Markdown格式的转换应用。
摘要由CSDN通过智能技术生成

现在,相关的基础知识我们应该有个大概的了解了,但离我们真正的开发出一个实用型的组件还有一段距离,不过不用担心,我们离目标已经越来越近。
以现在我们所了解的内容而言,或许你发现了一个问题,就是我们的编辑器的内容如何保存的问题,数据的保存是最重要的一个环节,无法保存的数据意义不大。我们以本地数据持久化为例来说明Slate中的这一相关功能。
到目前为止,我们的编辑一但在页面刷新的情况下就会还原到初始状态,即使我们做了诸多的内容编辑也会付诸东流。我们以本地存储为例,为<Slate/>
组件添加onChange事件,如下所示:

SDocer.jsx:

import { useState, useCallback } from 'react';
import { createEditor, Editor, Transforms, Element } from 'slate';
import { Slate, withReact, Editable } from 'slate-react';

import { initialValue } from './_configure';
import { renderElement } from './_elementRender';
import { renderLeaf } from './_leafRender';
import { Helper } from './_helper';

function SDocer() {
  const [editor] = useState(() => withReact(createEditor()));
  return (
    <Slate
      editor={editor}
      initialValue={initialValue}
      onChange={value => {
        const isAstChange = editor.operations.some(
          op => 'set_selection' !== op.type
        )
        if (isAstChange) {
          const content = JSON.stringify(value)
          localStorage.setItem('content', content)
        }
      }}
    >
      <Editable
        renderElement={useCallback(renderElement, [])}
        renderLeaf={useCallback(renderLeaf, [])}
        onKeyDown={event => {
          if (!event.ctrlKey) return;

          switch (event.key) {
            case '`': {
              event.preventDefault()
              Helper.toggleCodeBlock(editor);
              break
            }

            case 'b': {
              Helper.toggleBoldMark(editor);
              break
            }
          }
        }}
      />
    </Slate>
  )
}

export default SDocer;

这时候当我们键入任何内容后在 localStoragecontent 中的内容都能看到变化。如下所示:

在这里插入图片描述

虽然现在我们的内容能够实时的保存,但是页面一刷新还是还原了,这是显而易见的,因为我们并没有在组件初始化时从我们的LocalStore中读取数据,所以就只显示初始变量中的内容。我们调入localStorage中的内容就行了:

const initDatas = () => JSON.parse(localStorage.getItem('content')) || initialValue;

并把这个内容用useMemo无依赖的静态化,如下所示:

import { useState, useCallback, useMemo } from 'react';
import { createEditor, Editor, Transforms, Element } from 'slate';
import { Slate, withReact, Editable } from 'slate-react';

import { initialValue } from './_configure';
import { renderElement } from './_elementRender';
import { renderLeaf } from './_leafRender';
import { Helper } from './_helper';

const initDatas = () => JSON.parse(localStorage.getItem('content')) || initialValue;

function SDocer() {
  const [editor] = useState(() => withReact(createEditor()));

  return (
    <Slate
      editor={editor}
      initialValue={useMemo(initDatas, [])}
      onChange={value => {
        const isAstChange = editor.operations.some(
          op => 'set_selection' !== op.type
        )
        if (isAstChange) {
          const content = JSON.stringify(value)
          localStorage.setItem('content', content)
        }
      }}
    >
      <Editable
        renderElement={useCallback(renderElement, [])}
        renderLeaf={useCallback(renderLeaf, [])}
        onKeyDown={event => {
          if (!event.ctrlKey) return;

          switch (event.key) {
            case '`': {
              event.preventDefault()
              Helper.toggleCodeBlock(editor);
              break
            }

            case 'b': {
              Helper.toggleBoldMark(editor);
              break
            }
          }
        }}
      />
    </Slate>
  )
}

export default SDocer;

这个时候当你编辑后再刷新页面,内容就不在发生变化了。这样的json数据很适用,利于网络传输。但有时你可能特立独行,就是要走不一样的道路,也是可以的,我们可以自定义序列化 serialize 和 反序列化 deserialize ,比如我想保存一个纯文格式,或许就要这样做了:
新建一个工具文件 _untils.jsx

import { Node } from 'slate'

export const serialize = value => {
    return (
        value
            .map(n => Node.string(n))
            .join('\n')
    )
}

export const deserialize = string => {
    const content = string || ''
    return content.split('\n').map(line => {
        return {
            children: [{ text: line }],
        }
    })
}

上面的工具很简单,就是把所有的节点纯文本化。以换行符分割。把上面的工具应用于Slate,如下所示:

import { useState, useCallback, useMemo } from 'react';
import { createEditor, Editor, Transforms, Element } from 'slate';
import { Slate, withReact, Editable } from 'slate-react';

import { initialValue } from './_configure';
import { renderElement } from './_elementRender';
import { renderLeaf } from './_leafRender';
import { Helper } from './_helper';
import { serialize, deserialize } from './_untils';

// const initDatas = () => JSON.parse(localStorage.getItem('content')) || initialValue;
const initDatas = () => deserialize(localStorage.getItem('content')) || "";

function SDocer() {
  const [editor] = useState(() => withReact(createEditor()));

  return (
    <Slate
      editor={editor}
      initialValue={useMemo(initDatas, [])}
      onChange={value => {
        const isAstChange = editor.operations.some(
          op => 'set_selection' !== op.type
        )
        if (isAstChange) {
          // const content = JSON.stringify(value)
          localStorage.setItem('content', serialize(content))
        }
      }}
    >
      <Editable
        renderElement={useCallback(renderElement, [])}
        renderLeaf={useCallback(renderLeaf, [])}
        onKeyDown={event => {
          if (!event.ctrlKey) return;

          switch (event.key) {
            case '`': {
              event.preventDefault()
              Helper.toggleCodeBlock(editor);
              break
            }

            case 'b': {
              Helper.toggleBoldMark(editor);
              break
            }
          }
        }}
      />
    </Slate>
  )
}

export default SDocer;

结果符合预期。相似的做法,我也可以将内容序列化HTML、Markdown 等等,一切皆有可能。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码蚁先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值