Как создать динамический список из input, что бы вводимые значения не дублировались и вели себя как отдельные элементы?
Задача - вывести некоторые данные по get запросу c нескольких произвольных url.
Для этого я хочу создать динамический список из input элементов.
Я реализовал кнопки добавления новых input и удаления предыдущих
После добавления новых инпутов и последующем вводе данных в любой из них, во всех остальных введенные данные также сразу появляются.
const initialUrl = [
{
url: ''
}
]
const newUrl= {
url: ''
}
const initialData = [
{
data: ''
}
]
const newData = {
data: ''
}
function App() {
const [value, setValue] = useState('')
const [errorMessage, setErrorMessage] = useState('')
const [url, setUrl] = useState(initialUrl)
const [data, setData] = useState(initialData)
const [result, setResult] = useState()
function handleChange(event) {
setValue(event.target.value)
}
function addField () {
setUrl(url.length < 10 ? [...url, newUrl] : url)
setData([...data, newData])
}
function removeField () {
setUrl(url.length > 1 ? url.filter((note, index) => index !== url.length - 1) : url)
setData(data.filter((note, index) => index !== url.length - 1))
}
async function handleClick() {
if (validator.isURL(value)) {
await axios.get(value)
.then(response => {
if (response.status === 200) {
setResult(response.headers)
}})
.catch(function (error) {
if (error.response) {
setResult(error.response.status)
}})
} else {
setErrorMessage(' Некорректный URL')
}
}
return <div>
<div>
<h1>URLs</h1>
<div>
{url.map((item, index) => (
<div key={index}>
<input value={value} onChange={handleChange} />
{errorMessage}
</div>
))}
</div>
<div>
<button onClick={removeField}>
Remove field
</button>
</div>
<div>
<button onClick={addField}>
Add Field
</button>
</div>
<div>
<button onClick={handleClick}>
Get data
</button>
</div>
</div>
<h1>Answers</h1>
<div>
{data.map((item, index) => (
<div key={index}>
Результат запроса: {result}
</div>
))}
</div>
</div>;
}
Как реализовать динамический список в React.JS, что бы каждый новый input вел себя как отдельный элемент?
Ответы (1 шт):
Для создания динамического списка, необходимо для каждого нового элемента этого списка организовать свое хранилище данных.
В большинстве случаев, для этого идеально подходит массив.
Создадим шаблон для новых элементов. Делаем это так, что бы в каждом элементе были все необходимые значения.
const newUrl = {
id: 0,
url: "",
errorMessage: "",
result: "",
data: ""
};
Наш список будет представлен массивом объектов newUrl.
Массив будем хранить в переменной состояния value
Для создания нового input будем пушить(добавлять) новый элемент в value. Функция
addField()function addField() { if (value.length < 10) { const added = [...value]; // Получаем текущее состояние const data = { ...newUrl }; // Получаем шаблон элемента data.id = lastId + 1; // Записываем новый ID added.push(data); // Добавляем элемент setId((e) => ++e); // Увеличиваем ID setValue(added); // Обновляем состояние } }При удалении - удалять элемент методом filter(). Функция
removeField(index)- в качестве параметра передаем индекс массива для исключения.function removeField(index) { // Фильтруем массив по индексу элемента setValue(value.filter((item, idx) => index !== idx)); }При получении данных и изменения значений в input будем менять элемент массива. Функция
handleChange(event, index)будет отвечать за изменение данных внутри input, а функцияhandleClick(index, url)будет отвечать за получение данных от внешнего API и сохранения состояния.function handleChange(event, index) { const valueNew = [...value]; // Получаем текущее состояние valueNew[index].url = event; // Меняем значение setValue(valueNew); // Сохраняем состояние }async function handleClick(index, url) { if (validator.isURL(url)) { await axios .get(url) .then((response) => { if (response.status === 200) { const resNew = [...value]; // Получаем текущее состояние resNew[index].result = response.headers; // Меняем значение setValue(resNew); // Сохраняем состояние } }) .catch(function (error) { if (error.response) { const resNew = [...value]; resNew[index].result = error.response.status; } }); } else { const resNew = [...value]; resNew[index].errorMessage = "Некорректный URL"; setValue(resNew); } }
Теперь соберем все это вместе и получим необходимый нам компонент с реализованным динамическим списком input'ов
export default function App() {
const [value, setValue] = useState([{ ...newUrl }]);
const [lastId, setId] = useState(0);
function handleChange(event, index) {
const valueNew = [...value];
valueNew[index].url = event;
setValue(valueNew);
}
function addField() {
if (value.length < 10) {
const added = [...value];
const data = { ...newUrl };
data.id = lastId + 1;
added.push(data);
setId((e) => ++e);
setValue(added);
}
}
function removeField(index) {
setValue(value.filter((item, idx) => index !== idx));
}
async function handleClick(index, url) {
if (validator.isURL(url)) {
await axios
.get(url)
.then((response) => {
if (response.status === 200) {
const resNew = [...value];
resNew[index].result = response.headers;
setValue(resNew);
}
})
.catch(function (error) {
if (error.response) {
const resNew = [...value];
resNew[index].result = error.response.status;
}
});
} else {
const resNew = [...value];
resNew[index].errorMessage = "Некорректный URL";
setValue(resNew);
}
}
return (
<div>
<div>
<h1>URLs</h1>
<div style={{ width: 500 }}>
{value.map((item, index) => (
<div key={index} style={{ width: 500 }}>
{item.id + " "}
<input
value={item.url}
onChange={(e) => handleChange(e.target.value, index)}
/>{" "}
<button onClick={() => handleClick(index, item.url)}>
Get data
</button>{" "}
<button onClick={() => removeField(index)}>Remove field</button>
{item.errorMessage}
</div>
))}
</div>
<br />
<br />
<div>
<button onClick={addField}>Add Field</button>
</div>
</div>
<h1>Answers</h1>
<div>
{value.map((item, index) => (
<div key={index}>
Результат запроса {item.id}: {item.result}
</div>
))}
</div>
</div>
);
}
Если данные элементов не нужно собирать в одном компоненте, то input'ы можно вынести в отдельные компоненты с созданием собственного состояния, это предотвратит перерисовку родительского компонента и всех дочерних. Пример того, как вынести элемент списка в отдельный компонент
Зачем используется spread оператор для обновления состояния компонента в React?
