React ul contenteditable 的状态管理问题

问题描述 投票:0回答:1

我正在尝试创建一个自定义表单字段,将字符串数组显示为可编辑的

ul
列表并进行编辑 - 该字段为数组中的每个字符串项创建一个项目符号。

问题是当我尝试使用

value
onChange
属性(如常规表单字段)管理字段的状态时 - 该字段复制了正在显示的值,因此,它还使用重复项。

这是重现该问题的一段代码:

import { useMemo, useState, type FormEvent } from 'react';

export default function List() {
  const [value, setValue] = useState<string[]>(['first line', 'second line']);
  const listItems = useMemo(
    () => Array.from(Array.isArray(value) ? value : [value]),
    [value]
  );

  function handleInput(event: FormEvent) {
    const items = event.currentTarget.innerHTML
      .split(/<li>(.*?)<\/li>/)
      ?.map((item: string) => item.replace(/<(.*?)>/g, ''))
      ?.filter(Boolean);
    setValue(items);
  }

  return (
    <ul
      contentEditable
      suppressContentEditableWarning
      onInput={handleInput}
    >
      {listItems.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}

我使用本地状态来演示该问题,但实际上,

value
onChange
将作为道具注入

我在玩这个时得出的一些结论:

  1. 有一个 React 内部状态可以完美管理
    ul
    元素内部 HTML。我使用的临时解决方案是在
    onChange
    ul
    事件上调用
    onBlur
  2. 当我使用
    dangerouslySetInnerHTML
    并将
    li
    设置为字符串时 - 光标会跳到每个笔划的列表开头,并且我无法设置光标位置,因为我没有对当前编辑行的引用- 但这是一个问题或另一个问题

谢谢

reactjs contenteditable
1个回答
0
投票

使用react-contenteditable

EditableListField 组件有两个 props:value 和 onChange。 value 是表示列表项的字符串数组,onChange 是更新列表项的函数。 EditableListField 组件维护一个状态变量 html,用于存储列表的 HTML 表示形式。 当 value 属性更改时,useEffect 挂钩会更新 html 以反映新的列表项。 当用户编辑列表时,会触发handleChange函数。它解析更新的 HTML,提取列表项,并使用新列表项调用 onChange 属性。

App组件维护一个存储当前列表项的状态变量listItems。它渲染 EditableListField 组件并传递 listItems 和 setter 函数 setListItems 作为 props。

import "./styles.css";

import React, { useState, useEffect, ChangeEvent } from "react";
import ContentEditable from "react-contenteditable";

interface EditableListFieldProps {
  value: string[];
  onChange: (newItems: string[]) => void;
}

const EditableListField: React.FC<EditableListFieldProps> = ({ value, onChange }) => {
  const [html, setHtml] = useState<string>("");

  useEffect(() => {
    const listHtml = value.map((item) => `<li>${item}</li>`).join("");
    setHtml(`<ul>${listHtml}</ul>`);
  }, [value]);

  const handleChange = (event: ChangeEvent<HTMLDivElement>) => {
    const parser = new DOMParser();
    const doc = parser.parseFromString(event.currentTarget.innerHTML, "text/html");
    const items = Array.from(doc.querySelectorAll("li")).map(
      (li) => li.textContent || ""
    );
  
    onChange(items);
    setHtml(event.currentTarget.innerHTML);
  };
  

  return <ContentEditable html={html} onChange={handleChange} tagName="div" />;
};

const App: React.FC = () => {
  const [listItems, setListItems] = useState<string[]>(["Item 1", "Item 2", "Item 3"]);

  const handleListChange = (newItems: string[]) => {
    setListItems(newItems);
  };

  return (
    <div>
      <label>Editable List:</label>
      <EditableListField value={listItems} onChange={handleListChange} />
    </div>
  );
};

export default App;

如果你想玩一下代码

© www.soinside.com 2019 - 2024. All rights reserved.