我正在尝试创建一个自定义表单字段,将字符串数组显示为可编辑的
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
将作为道具注入
我在玩这个时得出的一些结论:
ul
元素内部 HTML。我使用的临时解决方案是在 onChange
的 ul
事件上调用 onBlur
。dangerouslySetInnerHTML
并将 li
设置为字符串时 - 光标会跳到每个笔划的列表开头,并且我无法设置光标位置,因为我没有对当前编辑行的引用- 但这是一个问题或另一个问题谢谢
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;
如果你想玩一下代码