Ссылка на Codesandbox: https://codesandbox.io/s/tender-meadow-un6ny7?file=/src/index.js
Я создаю базовое приложение, в которое вы можете динамически добавлять текстовые поля с помощью библиотеки react-draft-wysiwyg.
return (
<div className = "editor">
<Editor
//Update and show text box content
editorState = {editorState}
onEditorStateChange = {(editorState) => {
let html = stateToHTML(editorState.getCurrentContent());
console.info(html);
setHtml(html);
props.setContent(editorId, html, editorState);
setEditorState(editorState);
}}
//Display toolbar on top
toolbar = {{
inline: { inDropdown: true },
list: { inDropdown: true },
textAlign: { inDropdown: true },
link: { inDropdown: true },
history: { inDropdown: true },
}}
/>
</div>
);
};
Текстовые поля сохраняются и обновляются через хук состояния React. Один контролирует фактическое отображаемое состояние каждого текстового поля (editorState), а другой хранит необработанный html содержимого каждого текстового поля (html). Хуки состояния уже имеют определенное поведение для добавления, удаления и редактирования.
const EditorContent = (props) => {
const [editorState, setEditorState] = useState(props.content);
const [html, setHtml] = useState("");
useEffect(() => {
console.info("loaded number " + props.id);
});
function App() {
//Declaration of editorList array and setList setter
const [editorList, setEditorList] = useState([
{ id: 0, html: "", content: EditorState.createEmpty() },
]);
const [numberofEditors, setNumberOfEditors] = useState(0);
//Lets you view editorList content in inspect tab
console.info(editorList);
//Used to find specific instance, not used in current code
//Refer to video, forgot what it's used for
const handleServiceChange = (e, index) => {
const { name, value } = e.target;
console.info(name, value);
const list = [...editorList];
list[index][name] = value;
//setEditorList(list);
};
//Handles remove function
const handleEditorRemove = (id) => {
console.info("Remove id " + id);
// remove editor with designated id
let list = editorList.filter((editor) => {
return editor.id !== id;
});
console.info("Updated List: " + list);
setEditorList(list);
};
const handleEditorAdd = (id) => {
console.info(id);
setEditorList([
...editorList,
{ id: id, html: "", content: EditorState.createEmpty() },
]);
setNumberOfEditors(numberofEditors + 1);
};
const setEditorContent = (id, html, editorState) => {
console.info(id, html);
// deep copy array
let editorsCopy = [];
for (let editor of editorList) {
editorsCopy.push(editor);
}
const index = editorsCopy.findIndex((editor) => {
return editor.id === id;
});
editorsCopy[index].html = html;
editorsCopy[index].content = editorState;
setEditorList(editorsCopy);
};
Каждое текстовое поле также отображается с двумя кнопками, одна из которых позволяет добавить другое поле, а другая — удалить этот конкретный компонент.
return (
<form className = "App" autoComplete = "off">
<div className = "form-field">
<label htmlFor = "editor">Editor(s)</label>
{editorList.map((editor, index) => (
<div key = {index} className = "services">
<div className = "first-division">
<EditorContent id = {editor.id} setContent = {setEditorContent} />
{editorList.length - 1 === index && editorList.length < 4 && (
<button
type = "button"
onClick = {() => {
handleEditorAdd(numberofEditors + 1);
}}
className = "add-btn"
>
<span>Add an Editor</span>
</button>
)}
</div>
<div className = "second-division">
{editorList.length !== 1 && (
<button
type = "button"
onClick = {() => {
handleEditorRemove(editor.id);
}}
className = "remove-btn"
>
<span>Remove</span>
</button>
)}
</div>
</div>
))}
</div>
<div className = "output">
<h2>Output</h2>
{editorList &&
editorList.map((editor, index) => (
<ul key = {index}>{editor.service && <li>{editor.content}</li>}</ul>
))}
</div>
</form>
);
}
Добавление новых текстовых полей и обновление сохраненного html работает отлично. Однако, когда я пытаюсь удалить конкретное текстовое поле, независимо от того, какую кнопку удаления я выбираю, всегда удаляется последняя строка. Я попытался добавить ключи идентификатора для каждого текстового поля и сплайсинга, но, похоже, ничто не правильно отображает изменения, несмотря на то, что массив HTML отображает правильный текст.
Я неправильно обновляю состояние? Или просто рендерит неправильно? Мы будем признательны за любой вклад.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


единственная ошибка, которую я вижу в вашем коде, это то, что вы используете
key = {index}
при рендеринге элементов списка и согласно официальному документу
Лучший способ выбрать ключ — использовать строку, которая однозначно идентифицирует элемент списка среди его братьев и сестер. Чаще всего в качестве ключей используются идентификаторы из ваших данных.
поэтому вместо использования индекса вы должны использовать идентификатор редактора, это заставит его работать правильно, например
{editorList.map((editor, index) => (
<div key = {editor.id} className = "services">
<div className = "first-division">
<EditorContent id = {editor.id} setContent = {setEditorContent} />
{editorList.length - 1 === index && editorList.length < 4 && (
<button
type = "button"
onClick = {() => {
handleEditorAdd(numberofEditors + 1);
}}
className = "add-btn"
>
<span>Add an Editor</span>
</button>
)}
</div>
<div className = "second-division">
{editorList.length !== 1 && (
<button
type = "button"
onClick = {() => {
handleEditorRemove(editor.id);
}}
className = "remove-btn"
>
<span>Remove</span>
</button>
)}
</div>
</div>
))}
Вау, это действительно сработало! Был еще один поток с уникальными ключами, и я, должно быть, слишком зациклился на идентификаторах. Большое спасибо за быстрый ответ.