微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 Draft.JS 编辑器和 Formik 中使用 useImperativeHandle 钩子是否合理?

如何解决在 Draft.JS 编辑器和 Formik 中使用 useImperativeHandle 钩子是否合理?

我正在用 React 制作一个小型 CMS 系统,我有一个表单,用户可以在其中使用 Draft.js 编辑器和其他一些字段。对于这个问题,让我们关注编辑表单。

编辑器的代码如下所示:

import React,{ useRef } from 'react';
import { Formik } from "formik";
import TextInputField from "@/components/TextInputField";
import client from "@/utils/http";

const MyForm = ({title,content}) => {
   const editorRef = useRef();
  
  function handleSubmit(values) {
     const editorContent = editorRef.current.parse();
     
     client.submit('/api/edit/project',{ editorContent,...values });
  }

  return (
    <Formik onSubmit={formik.handleSubmit} initialValues={{ title }}>
     {
       (formik) => (
           <form onSubmit={formik.handleSubmit}>
              <TextInputField label="title" name="title" />

              <RichEditor ref={editorRef} content={content} />
           </form>
     )}
  </Formik>);
}

我有编辑器代码

import React,{ useImperativeHandle,useState } from "react";
import {
  Editor,EditorState,convertFromHTML,ContentState,convertToRaw,} from "draft-js";
import draftToHtml from "draftjs-to-html";

function createFromContent(htmlContent) {
  const blocksFromHtml = convertFromHTML(htmlContent);

  const editorState = ContentState.createFromBlockArray(
    blocksFromHtml.contentBlocks,blocksFromHtml.entityMap
  );

  return EditorState.createWithContent(editorState);
}

function formatToHTML(editorState) {
  const raw = convertToRaw(editorState.getCurrentContent());
  const markup = draftToHtml(raw);
  return markup;
}

function RichEditor({ content = null },ref) {
  const [editorState,setEditorState] = useState(() =>
    content ? createFromContent(content) : EditorState.createEmpty()
  );

  useImperativeHandle(
    ref,() => ({
      parse: () => {
        return formatToHTML(editorState);
      },}),[editorState]
  );

  return (
    <div className="App-Rich-Editor w-full block border border-gray-300 rounded-md mt-4 shadow-sm">
      <Editor
        placeholder="Enter your content..."
        editorState={editorState}
        onChange={setEditorState}
      />
    </div>
  );
}

export default React.forwardRef(RichEditor);

有效,但它给我带来了以下问题,因此为什么要问社区,因为使用 useImperativeHandle 似乎是一种“黑客”。因为即使是 React 文档也不鼓励使用它。

一如既往,在大多数情况下应该避免使用引用的命令式代码

因为我想格式化编辑器的内部状态仅一次,所以当我提交表单时,我显示代码是否合理,即使它“逆势而上”,通过使用命令句柄与父级共享子状态。

这让我想到了问题:

  • 在这种情况下是否可以 useImperativeHandle 挂钩以进行“优化”,以便我们仅在需要时获取状态?
  • 是否有一些更好的方法可以使用“常见”模式(例如“提升状态”、“渲染道具”或其他方式)来实现此实现?
  • 我是否忽略了此处的问题,我是否应该硬着头皮将整个编辑器状态与 formik 同步,方法是将其从组件中提起,然后在提交时对其进行格式化?

对我来说,第三个选项似乎打破了关注点的分离,因为它会用状态逻辑污染 Form 上下文,感觉它不属于那里。

解决方法

以我的拙见,提供的解决方案有点过度设计。因此,让我就您提出的问题提供我的想法:

  • 我没有看到使用 useImperativeHandle 的优化,因为值同时存储在 ref 和 RichEditor 状态
  • formatToHTML 函数似乎是纯函数。那么为什么不导出它并在表单提交之前使用,而不是使用 forwardRefuseImperativeHandle
  • 这是我的建议,我认为这正是您在第三个要点中提到的:
import TextInputField from "@/components/TextInputField";
import client from "@/utils/http";
import {
  ContentState,convertFromHTML,convertToRaw,Editor,EditorState,} from "draft-js";
import draftToHtml from "draftjs-to-html";
import { Formik } from "formik";
import React,{ useCallback } from "react";

function createFromContent(htmlContent) {
  const blocksFromHtml = convertFromHTML(htmlContent);

  const editorState = ContentState.createFromBlockArray(
    blocksFromHtml.contentBlocks,blocksFromHtml.entityMap
  );

  return EditorState.createWithContent(editorState);
}

function formatToHTML(editorState) {
  const raw = convertToRaw(editorState.getCurrentContent());
  const markup = draftToHtml(raw);
  return markup;
}

const MyForm = ({ title,content }) => {
  const [editorState,setEditorState] = useState(() =>
    content ? createFromContent(content) : EditorState.createEmpty()
  );

  const handleSubmit = useCallback(
    (values) => {
      const editorContent = formatToHTML(editorState);

      client.submit("/api/edit/project",{ editorContent,...values });
    },[editorState]
  );

  return (
    <Formik onSubmit={handleSubmit} initialValues={{ title }}>
      {(formik) => (
        <form onSubmit={formik.handleSubmit}>
          <TextInputField label="title" name="title" />

          <div className="App-Rich-Editor w-full block border border-gray-300 rounded-md mt-4 shadow-sm">
            <Editor
              placeholder="Enter your content..."
              editorState={editorState}
              onChange={setEditorState}
            />
          </div>
        </form>
      )}
    </Formik>
  );
};

export default MyForm;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。