Узнайте, как оптимизировать производительность форм реагирования, заменив useState
встроенной функцией JavaScript.
Введение
Когда дело доходит до обработки форм в реакции, наиболее популярным подходом является сохранение входных значений в переменных состояния. Одна из причин следовать этому подходу заключается в том, что, в конце концов, это React, и все склонны использовать связанные с ним хуки. Использование хуков решает множество проблем в React, но действительно ли это необходимо, когда дело касается форм? Давайте проверим это.
Проблема с использованием состояний
Как мы уже знаем, всякий раз, когда значение переменной состояния изменяется внутри компонента, реакция будет повторно отображать компонент, чтобы он соответствовал его текущему состоянию. Хотя это не является большой проблемой для небольших приложений, это может привести к снижению производительности по мере увеличения размера вашего приложения.
Когда дело доходит до формы, реакция будет пытаться повторно отобразить компонент каждый раз, когда изменяется ввод (состояние).
Боковой совет: я наткнулся на этот ответ на StackOverflow, который очень полезен для подсчета количества рендерингов компонента. Мы также будем использовать эту служебную функцию в нашем коде.
Давайте реализуем и посмотрим на проблему с состояниями в действии.
Создайте базовое приложение реагирования, используя vite, и очистите ненужные файлы после создания проекта.
npm create vite@latest my-vue-app -- --template react # yarn yarn create vite my-vue-app --template react # pnpm pnpm create vite my-vue-app --template react
Давайте создадим компонент реагирования (скажем, FormWithState
), содержащий форму, которая принимает два входных параметра email
и password
. Мы будем использовать useState
для управления вводом формы.
import { useEffect, useRef, useState } from "react"; export default function FormWithState() { // The count will increment by 2 on initial render due to strict mode then by 1 on subsequent renders const countRef = useRef(0); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); useEffect(() => { countRef.current = countRef.current + 1; }); function handleSubmit(e) { e.preventDefault(); console.log({ email, password }); } return ( <div className="form-div"> <h2>Form With State</h2> <form onSubmit={handleSubmit}> <div className="input-field"> <label htmlFor="email2">Email</label> <input id="email2" type="email" value={email} onChange={(e) => setEmail(e.target.value)} autoComplete="off" /> </div> <div className="input-field"> <label htmlFor="password2">Password</label> <input id="password2" type="password" value={password} onChange={(e) => setPassword(e.target.value)} /> </div> <button type="submit">Submit</button> <div> <p> The Component Re-Rendered <span>{countRef.current}</span> times </p> </div> </form> </div> ); }
Добавьте этот компонент в компонент App
и откройте http://localhost:5173
.
Как видите, компонент формы отображается примерно 23 раза, и количество будет постепенно увеличиваться по мере увеличения количества полей ввода. В большинстве случаев значения формы используются только во время отправки формы. Итак, нужно ли перерисовывать компонент более 20 раз только для двух полей ввода? Ответ однозначный: НЕТ!
Кроме того, когда количество полей ввода увеличивается, количество переменных состояния для хранения входных значений также увеличивается, тем самым увеличивая сложность кодовой базы.
Итак, каков альтернативный подход, позволяющий избежать повторного рендеринга, но сохранить все функциональные возможности форм?
Использование FormData для обработки форм
Итак, альтернативный подход — использовать собственный интерфейс FormData
JavaScript.
Есть три способа создать новый объект FormData
, как описано в официальной документации.
new FormData() new FormData(form) new FormData(form, submitter)
Мы будем использовать второй метод, потому что форма у нас уже есть. Нам просто нужно передать элемент формы конструктору, и он автоматически заполнит значения формы. Чтобы это работало, нам также нужно добавить атрибут name
к тегу input
. Давайте проверим этот подход. Создайте компонент (скажем, FormWithoutState
).
import { useEffect, useRef } from "react"; export default function FormWithoutState() { // The count will increment by 2 on initial render due to strict mode then by 1 on subsequent renders const countRef = useRef(0); useEffect(() => { countRef.current = countRef.current + 1; }); function handleSubmit(e) { e.preventDefault(); const form = new FormData(e.currentTarget); const body = {}; for (const [key, value] of form.entries()) { body[key] = value; } console.log(body); // Do Further input validation and submit the form } return ( <div className="form-div"> <h2>Form Without State</h2> <form onSubmit={handleSubmit}> <div className="input-field"> <label htmlFor="email1">Email</label> <input id="email1" type="email" name="email" autoComplete="off" /> </div> <div className="input-field"> <label htmlFor="password1">Password</label> <input id="password1" type="password" name="password" /> </div> <button type="submit">Submit</button> <div> <p> The Component Re-Rendered <span>{countRef.current}</span> times </p> </div> </form> </div> ); }
В этом компоненте мы вообще не использовали хукuseState
. Вместо этого мы добавляем атрибут name
к тегу input
. Как только пользователь отправляет форму, в функции handleSubmit
мы создаем FormData
, предоставляя объект формы через e.currentTarget
. Затем мы повторяем метод FormData.entries()
, чтобы получить ключ и значение формы для построения тела формы. Затем мы можем использовать этот объект для дальнейшей проверки ввода и отправки через API fetch
или axios
. Но как насчет влияния повторного рендеринга компонентов при таком подходе? Давайте проверим это. Добавьте этот компонент в компонент App
и откройте http://localhost:5173
.
Вы не удивлены? Компонент вообще не перерисовывался.
Преимущества использования FormData
- Форма входные значения фиксируются автоматически без необходимости поддерживать переменную состояния для каждого поля ввода.
- Компонент не выполняет повторную визуализацию при вводе пользователя.
- Тело запроса API можно легко создать при использовании
FormData
, тогда как при использованииuseState
нам потребуется собрать данные для отправки. - Это устраняет необходимость введения новых переменных состояния по мере роста формы.
- Имея дело с несколькими формами, вы можете в конечном итоге дублировать одинаковые переменные состояния в разных компонентах, тогда как
FormData
можно легко использовать повторно с помощью всего лишь нескольких строк кода. - Одна вещь, которую
FormData
поддерживает «из коробки», — это автоматическая обработка динамических полей. т. е. если ваша форма имеет динамически генерируемые поля (добавление/удаление полей на основе ввода пользователя), управление их состоянием с помощьюuseState
требует дополнительной обработки, тогда какFormData
позаботится об этом автоматически.
Заключение
Вы можете проверить код этой статьи в песочнице кода здесь. Надеюсь, вы узнали что-то новое из этой статьи. Оставьте комментарий, если у вас есть сомнения. Спасибо за прочтение.
На простом английском языке
Спасибо, что вы являетесь частью нашего сообщества! Прежде чем уйти:
- Обязательно аплодируйте и следуйте за автором! 👏
- Еще больше контента вы можете найти на PlainEnglish.io 🚀
- Подпишитесь на нашу бесплатную еженедельную рассылку. 🗞️
- Следуйте за нами в Twitter, LinkedIn, YouTube > и Discord.