Универсальная форма для изменения массива в React

Давайте теперь сделаем так, чтобы с помощью одного инпута можно было и добавлять новые элементы в массив, и редактировать существующие. Пусть изначально инпут находится в режиме добавления, а клик по абзацу переводит инпут в режим редактирования.

Если инпут в режиме добавления, то по потери фокуса в массив добавится новый элемент. А если инпут в режиме редактирования, то абзац будет редактироваться одновременно с инпутом, а по потери фокуса инпут перейдет в режим добавления.

Для решения описанной задачи есть несколько подходов.

Подход первый

Сделаем два инпута и с помощью условного рендеринга будем показывать или инпут для добавления, или инпут для редактирования.

Вот реализация описанного:

function App() { const [notes, setNotes] = useState([1, 2, 3, 4, 5]); const [editNum, setEditNum] = useState(null); const [value, setValue] = useState(''); const result = notes.map((note, index) => { return <p key={index} onClick={() => setEditNum(index)}> {note} </p>; }); function changeItem(event) { setNotes([...notes.slice(0, editNum), event.target.value,...notes.slice(editNum + 1)]); } function stopEdit(event) { setEditNum(null); } function changeValue(event) { setValue(event.target.value) } function addItem(event) { setNotes([...notes, value]); } let input; if (editNum) { input = <input value={notes[editNum]} onChange={changeItem} onBlur={stopEdit} /> } else { input = <input value={value} onChange={changeValue} onBlur={addItem} /> } return <div> {result} {input} </div>; }

Подход второй

Совместим обе операции в одном инпуте:

function App() { const [notes, setNotes] = useState([1, 2, 3, 4, 5]); const [editNum, setEditNum] = useState(null); const [value, setValue] = useState(''); const result = notes.map((note, index) => { return <p key={index} onClick={() => startEdit(index)}> {note} </p>; }); function startEdit(index) { setEditNum(index); setValue(notes[index]); } function changeHandler(event) { setValue(event.target.value); if (editNum) { setNotes([...notes.slice(0, editNum), event.target.value,...notes.slice(editNum + 1)]); } } function blurHandler(event) { if (!editNum) { setNotes([...notes, value]); } else { setEditNum(null); } setValue(''); } return <div> {result} <input value={value} onChange={changeHandler} onBlur={blurHandler} /> </div>; }

Подход третий

Сделаем так, чтобы при добавлении нового элемента он сразу появлялся в виде нового абзаца. И при наборе текста в инпуте в этом абзаце сразу набирался текст нового элемента.

Для этого при получении инпутом фокуса в режиме добавления будем сразу же добавлять новый элемент в конец массива и сразу же переходить в режим редактирования для этого элемента.

Реализуем:

function App() { const [notes, setNotes] = useState([1, 2, 3, 4, 5]); const [editNum, setEditNum] = useState(null); const result = notes.map((note, index) => { return <p key={index} onClick={() => startEdit(index)}>{note}</p>; }); function startEdit(index) { setEditNum(index); } function editItem(event) { setNotes([...notes.slice(0, editNum), event.target.value,...notes.slice(editNum + 1)]); } function createItem() { if (!editNum) { const res = [...notes, '']; setNotes(res); setEditNum(res.length - 1); } } function stopEdit() { setEditNum(null); } return <div> {result} <input value={editNum ? notes[editNum] : ''} onChange={editItem} onFocus={createItem} onBlur={stopEdit} /> </div>; }

Практические задачи

Дан массив:

const notes = ['a', 'b', 'c', 'd', 'e'];

Выведите элементы этого массива в виде списка ul. Под списком реализуйте инпут для редактирования существующих и добавления новых пунктов списка. Решите задачу тремя описанными подходами.

Расскажите, какие достоинства и недостатки присутствуют в описанных подходах.