Как предотвратить потерю фокуса при вводе в 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 шт):

Автор решения: 118_64

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

Было так:

 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 нет.

→ Ссылка