Как предотвратить потерю фокуса при вводе в input в ReactJS?
Есть форма, создаваемая с помощью React, при вводе в поле name которой значение записывается в состояние. После ввода любого символа поле теряет фокус. Я понимаю, что useState обновляет страницу, и причина в этом.
Для решения этой проблемы я присвоил ключи и полю input, и внешним элементам. Результат тот же. Пытался добавлять input как внешний элемент, но форма продолжает обновляться.
Весь код опубликован здесь: https://codesandbox.io/s/me08n
Прошу помощи и совета, как создать форму без перезагрузки при вводе. Ниже - код файла, создающего форму.
Form.js :
import axios from "axios";
function Form({ data, funct }) {
const HandleImageUpload = () => {
const handleImage = (e) => {
const target = imageLoader.files[0];
const type = target.type.split("/");
const typeArr = ["jpg", "jpeg", "gif", "png"];
if (!typeArr.includes(type[1])) {
funct.setButtonValue("Недопустимый формат");
return;
} else {
funct.setButtonValue("Изображение загружено");
}
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onload = function (event) {
const image = new Image();
image.src = event.target.result;
image.onload = function () {
let imgWidth = image.width;
let imgHeight = image.height;
const orientation = imgWidth / imgHeight;
if (imgHeight > 240 || imgWidth > 320) {
if (orientation <= 1 || imgHeight * (320 / imgWidth) > 240) {
imgWidth = imgWidth * (240 / imgHeight);
imgHeight = 240;
} else {
imgHeight = imgHeight * (320 / imgWidth);
imgWidth = 320;
}
}
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = imgWidth;
canvas.height = imgHeight;
ctx.drawImage(image, 0, 0, imgWidth, imgHeight);
const preview = new Image(imgWidth, imgHeight);
preview.src = canvas.toDataURL("image/jpeg", 1.0);
funct.setImage(preview.src);
};
};
};
const imageLoader = document.getElementById("image_loader");
imageLoader.addEventListener("change", handleImage, false);
imageLoader.click();
};
const handleNameValidation = (e) => {
if (e.target.value.match(/[^0-9A-Z a-z]/gi)) {
e.target.value = e.target.value.replace(/[^0-9A-Z a-z]/gi, "");
}
};
const handleEmailOnBlurValidation = (e) => {
const reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,})$/;
if (!e.target.value.match(reg) && e.target.value != "") {
funct.setErrors("Ошибка! Введите правильный email.");
return false;
}
const checkingMailOriginality = async () => {
axios.post(`/check`, {}).then((res) => {
if (res.data !== 0) {
funct.setErrors("Вы уже комментировали. Допустим только один отзыв!");
} else {
funct.setMail(data);
}
});
};
// checkingMailOriginality();
};
const handleHomePageValidation = (e) => {
e.target.value = e.target.value
.toLowerCase()
.replace("https://", "")
.replace("http://", "");
};
const handleFeedbackSend = async (e) => {
e.preventDefault();
if (!data.name || !data.mail || !data.feedback || data.errors) {
return;
}
const feedbackData = {
name: data.name,
mail: data.mail,
page: data.homePage,
feedback: data.feedback,
image: data.image
};
const path = "/add";
const response = await fetch(path, {
method: "POST",
headers: {
"Content-Type": "application/json;charset=utf-8"
},
body: JSON.stringify(feedbackData)
});
const result = await response.text();
document.location.href = "/";
};
const handleEmailOnInput = (e) => {
e.target.value = e.target.value.toLowerCase();
if (e.target.value.match(/[^@0-9a-z-\.]/gi)) {
e.target.value = e.target.value.replace(/[^@0-9a-z-\.]/gi, "");
}
};
return (
<div className="feedbackForm" key="formKey4">
<form onSubmit={handleFeedbackSend} key="formKey3">
<h2>Мой комментарий:</h2>
<i
id="closeForm"
className="material-icons closeForm"
onClick={() => funct.setShowComponent("startButton")}
>
clear
</i>
<div className="error">{data.errors}</div>
<span className="formContainer" key="formKey1">
<label key="lKey1">
Имя (на английском):
<input
key="key1"
name="name"
type="text"
maxLength="40"
required
placeholder="e.g. Piter"
defaultValue={data.name}
onInput={(e) => {
handleNameValidation(e);
funct.setName(e.target.value);
}}
/>
</label>
<label key="lKey2">
E-mail:
<input
key="key2"
id="email"
name="mail"
type="text"
required
maxLength="40"
placeholder="[email protected]"
defaultValue={data.mail}
onInput={(e) => {
handleEmailOnInput(e);
funct.setErrors("");
}}
onBlur={(e) => {
funct.setMail(e.target.value);
handleEmailOnBlurValidation(e);
}}
/>
</label>
<label key="lKey3">
Моя домашняя страница:
<input
key="key3"
name="page"
type="text"
maxLength="100"
placeholder="example.com"
defaultValue={data.homePage}
onInput={handleHomePageValidation}
onBlur={(e) => funct.setHomePage(e.target.value)}
/>
</label>
</span>
<label key="lKey4">
Текст комментария:
<textarea
key="key4"
className="textareaForm"
maxLength="800"
required
defaultValue={data.feedback}
onBlur={(e) => funct.setFeedback(e.target.value)}
></textarea>
</label>
<span className="formContainer" key="formKey2">
<input key="key5" id="image_loader" type="file" hidden />
<input
key="key6"
id="upload"
type="button"
className="buttonSubmit"
defaultValue={data.buttonValue}
onClick={HandleImageUpload}
/>
<input
key="key7"
type="button"
className="buttonSubmit"
defaultValue="Предварительный просмотр"
onClick={() => funct.setShowComponent("preview")}
/>
<input
key="key8"
id="feedback_send"
type="submit"
className="buttonSubmit"
defaultValue="Опубликовать"
/>
</span>
</form>
</div>
);
}
export default Form;
Ответы (1 шт):
Для того, чтобы избежать обновления формы, необходимо убрать вызов функции внутри рендера.
Было так:
const FeedbackForm = () =>
showComponent === "form" ? <Form data={data} funct={funct} /> : null;
const FeedbackPreview = () =>
showComponent === "preview" ? (
<FeedbackRowPreview data={data} setShowComponent={setShowComponent} />
) : null;
return (
<div className="App">
<h1>Гостевая книга</h1>
<main>
<StartButton />
<ul className="feedbackList">
<FeedbackPreview />
<FeedbackForm />
</ul>
</main>
</div>
);
и стало так:
const feedbackFormNode =
showComponent === "form" ? <Form data={data} funct={funct} /> : null;
const feedbackPreviewNode =
showComponent === "preview" ? (
<FeedbackRowPreview data={data} setShowComponent={setShowComponent} />
) : null;
return (
<div className="App">
<h1>Гостевая книга</h1>
<main>
{StartButtonNode}
<ul className="feedbackList">
{feedbackFormNode}
{feedbackPreviewNode}
</ul>
</main>
</div>
);
}
Перерисовки всей формы при вводе в input нет.