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

Я уже писал ранее о создании форм с компонентами пользовательского интерфейса материала, но я не хотел полагаться на пользовательский интерфейс материала в этом проекте. Вместо этого я перешел на использование простого HTML и Tailwind CSS для своего стиля. Идея получения информации от пользователя с помощью React аналогична предыдущему посту. Я использовал два input и один button с некоторыми слушателями React. Каждый раз, когда любое из полей input изменяется (что означает, что пользователь вводит информацию), я обновляю переменную состояния, чтобы отслеживать эти данные. Затем, когда пользователь щелкает button, у меня уже есть введенная информация, сохраненная в состоянии, которая будет использоваться в обработчике buttons onClick. Ниже приведен простой HTML-код, но через секунду я добавлю немного стиля Tailwind.

export default function SigninPage() {
  const [signInEmail, setSignInEmail] = useState('');
  const [signInPassword, setSignInPassword] = useState('');
  const [errorMessage, setErrorMessage] = useState('');

  function handleSignInEmailFieldChange(event) {
    event.preventDefault();
    setSignInEmail(event.target.value);
  }

  function handleSignInPasswordFieldChange(event) {
    event.preventDefault();
    setSignInPassword(event.target.value);
  }

  async function handleSignIn(event) {
    event.preventDefault();
    setErrorMessage('');

    try {
      // Sign In logic using signInEmail and signInPassword state
      setErrorMessage('There was an error signing in');
    } catch (err) {
      // Remediation logic
      setErrorMessage('There was an error signing in');
    }
  }

  return(
    <div>
      <label>Email address</label>
      <input
        type="email"
        placeholder=""
        value={signInEmail}
        onChange={(e) => handleSignInEmailFieldChange(e)}
      />
      <label>Password</label>
      <input
        type="password"
        placeholder=""
        value={signInPassword}
        onChange={(e) => handleSignInPasswordFieldChange(e)}
      />
      <button
        onClick={handleSignIn}
      >
        Sign In
      </button>
      <p>
        { errorMessage }
      </p>
    </div>
  );
}

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

export default function SigninPage() {
  const [signInEmail, setSignInEmail] = useState('');
  const [signInPassword, setSignInPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');

  function handleSignInEmailFieldChange(event) {
    event.preventDefault();
    setSignInEmail(event.target.value);
  }

  function handleSignInPasswordFieldChange(event) {
    event.preventDefault();
    setSignInPassword(event.target.value);
  }

  async function handleSignIn(event) {
    event.preventDefault();
    setErrorMessage('');
    setLoading(true);

    try {
      // Sign In logic using signInEmail and signInPassword state
      setErrorMessage('There was an error signing in');
      setLoading(false);
    } catch (err) {
      // Remediation logic
      setErrorMessage('There was an error signing in');
      setLoading(false);
    }
  }

  return(
    <div className="flex flex-col items-center justify-center text-center">
      <div className="lg:w-2/5 md:w-3/5 w-4/5">
        <label className="mt-6">Email address</label>
        <input
          type="email"
          className="mt-1 w-full rounded-xl border-gray-300 shadow-sm focus:border-purple-500 focus:ring focus:ring-purple-500 focus:ring-opacity-50"
          placeholder=""
          value={signInEmail}
          onChange={(e) => handleSignInEmailFieldChange(e)}
        />
        <label className="mt-6">Password</label>
        <input
          type="password"
          className="mt-1 w-full rounded-xl border-gray-300 shadow-sm focus:border-purple-500 focus:ring focus:ring-purple-500 focus:ring-opacity-50"
          placeholder=""
          value={signInPassword}
          onChange={(e) => handleSignInPasswordFieldChange(e)}
        />
        <button
          className={`
            bg-white mt-6 border rounded-xl border-gray-300 p-2 hover:bg-purple-500 hover:text-white
            ${
              loading ? "bg-purple-500 text-white animate-pulse" : ""
            }
          `}
          disabled={loading}
          onClick={handleSignIn}
        >
          Sign In
        </button>
        <p className="text-red-900">
          { errorMessage }
        </p>
      </div>
    </div>
  );
}

Стиль внешнего div центрирует и выравнивает все в столбце. Второй div устанавливает ограничение ширины полей для разных размеров экрана. Для label указано верхнее поле. Поля input имеют некоторый цвет по краям, когда они находятся в фокусе. Последний p, содержащий errorMessage, теперь будет отображать текст красным цветом. Наконец, button стилизован так, чтобы отображать цвет при наведении курсора или фокусе с некоторым дополнительным стилем после щелчка. Состояние loading включается при каждом щелчке по button внутри handleSignIn и выключается снова, когда логика входа завершается. Пока страница ожидает логики входа, кнопка будет оставаться цветной и пульсировать, показывая пользователю, что что-то происходит за кулисами. Для получения дополнительной информации о классах, пожалуйста, обратитесь к документации Tailwind.