Ich bin React-Neuling und lerne es durch einige praktische Projekte. Ich arbeite derzeit an der Formularverarbeitung und -validierung. Ich verwende die Form-Komponente von React Router in einem SPA und im Formular befindet sich ein FormGroup-Element, das Beschriftungseingaben und Fehlermeldungen rendert. Ich verwende auch meine eigene Eingabekomponente innerhalb der FormGroup-Komponente, um die Logik- und Statusverwaltung der im Formular verwendeten Eingaben zu trennen.
Also habe ich die Form-Komponente und die FormGroup-Komponente wie folgt auf der Beispiel-Anmeldeseite platziert:
pages/Login.js
import { useState } from 'react'; import { Link, Form, useNavigate, useSubmit } from 'react-router-dom'; FormGroup aus '../components/UI/FormGroup' importieren; Button aus '../components/UI/Button' importieren; Karte aus '../components/UI/Card' importieren; import './Login.scss'; Funktion LoginPage() { const navigation = useNavigate(); constsubmit = useSubmit(); const [isLoginValid, setIsLoginValid] = useState(false); const [isPasswordValid, setIsPasswordValid] = useState(false); var resetLoginInput = null; var resetPasswordInput = null; let isFormValid = false; if(isLoginValid && isPasswordValid) { isFormValid = true; } Funktion formSubmitHandler(event) { event.preventDefault(); if(!isFormValid) { zurückkehren; } resetLoginInput(); resetPasswordInput(); einreichen(event.currentTarget); } Funktion loginValidityChangeHandler(isValid) { setIsLoginValid(isValid); } Funktion passwortValidityChangeHandler(isValid) { setIsPasswordValid(isValid); } Funktion resetLoginInputHandler(reset) { resetLoginInput = zurücksetzen; } Funktion resetPasswordInputHandler(reset) { resetPasswordInput = zurücksetzen; } Funktion switchToSignupHandler() { navigieren('/signup'); } zurückkehren (); } Standard-LoginPage exportieren;Go CupMelden Sie sich bei Ihrem Go Cup-Konto an
Wie Sie im obigen Code sehen können, verwende ich die FormGroup-Komponente und übergebe die Eigenschaften onValidityChange
, um isValid< / Der aktualisierte Wert des Codes> Änderungen und Rücksetzfunktionen zum Zurücksetzen der Eingaben nach dem Absenden des Formulars usw. Verwenden Sie meinen benutzerdefinierten Hook useInput, um die Funktionen
isValid
und reset
zu erstellen. Ich übergebe den isValid-Wert, wenn sich der Wert ändert, und übergebe die Reset-Funktion von der Eingabekomponente mithilfe von Requisiten, die in der FormGroup-Komponente definiert sind. Ich verwende auch die Statuswerte isLoginValid
auf der Anmeldeseite, um den aktualisierten Statuswert isValid
zu speichern Komponente. Also habe ich Zustände in der Eingabekomponente definiert und sie mithilfe von Requisiten an die übergeordnete Komponente übergeben und ihre Werte in anderen Zuständen gespeichert, die in dieser übergeordneten Komponente erstellt wurden. Die Propellerbohrungen, die gerade stattfanden, bereiteten mir ein wenig Unbehagen.
Der Status wird innerhalb der Eingabekomponente verwaltet. Ich habe diese Status:
Ich kombiniere und wende einige Funktionen (z. B. die an die Eingabekomponente übergebene Validierungsfunktion) auf diese beiden Zustände an, um andere Variablenwerte zu erstellen und Informationen über die Eingabe und ihre Gültigkeit zu sammeln, z. B. ob der Wert gültig ist (isValid), ob eine Nachrichtenüberprüfung vorliegt (Nachricht), ob die Eingabe gültig ist (isInputValid = isValid || !isInputTouched
), um zu entscheiden, ob die Überprüfungsnachricht angezeigt werden soll.
Diese Zustände und Werte werden in einem benutzerdefinierten Hook verwaltet, den ich erstellt habe, useInput
, wie folgt:
hooks/use-state.js
import { useState, useCallback } from 'react'; Funktion useInput(validityFn) { const [value, setValue] = useState(''); const [isInputTouched, setIsInputTouched] = useState(false); const [isValid, message] = typeof validityFn === 'function' ? validityFn(value) : [true, null]; const isInputValid = isValid ||. const inputChangeHandler = useCallback(event => { setValue(event.target.value); if(!isInputTouched) { setIsInputTouched(true); } }, [isInputTouched]); const inputBlurHandler = useCallback(() => { setIsInputTouched(true); }, []); const reset = useCallback(() => { setValue(''); setIsInputTouched(false); }, []); zurückkehren { Wert, ist gültig, isInputValid, Nachricht, inputChangeHandler, inputBlurHandler, zurücksetzen }; } Standardwert exportieren useInput;
Ich verwende derzeit diesen benutzerdefinierten Hook in Input.js wie folgt:
components/UI/Input.js
import { useEffect } from 'react'; importiere useInput aus '../../hooks/use-input'; import './Input.scss'; Funktion Input(props) { const { Wert, ist gültig, isInputValid, Nachricht, inputChangeHandler, inputBlurHandler, zurücksetzen } = useInput(props.validity); const { onIsInputValidOrMessageChange, onValidityChange, onReset } = Requisiten; let className = 'form-control'; if(!isInputValid) { className = `${className} form-control--invalid`; } if(props.className) { className = `${className} ${props.className}`; } useEffect(() => { if(onIsInputValidOrMessageChange && typeof onIsInputValidOrMessageChange === 'function') { onIsInputValidOrMessageChange(isInputValid, message); } }, [onIsInputValidOrMessageChange, isInputValid, message]); useEffect(() => { if(onValidityChange && typeof onValidityChange === 'function') { onValidityChange(isValid); } }, [onValidityChange, isValid]); useEffect(() => { if(onReset && typeof onReset === 'function') { onReset(reset); } }, [onReset, zurücksetzen]); zurückkehren (); } Standardeingabe exportieren;
In der Eingabekomponente verwende ich direkt den Status isInputValid
, um die ungültige CSS-Klasse zur Eingabe hinzuzufügen. Ich übergebe aber auch die Funktionen isInputValid
, message
und reset
an die übergeordnete Komponente darin zu verwenden. Um diese Zustände und Funktionen zu übergeben, verwende ich die in props definierten Funktionen onIsInputValidOrMessageChange
, onValidityChange
(Props-Drilldown, aber Richtung). Kind zu Eltern).
Dies ist die Definition der FormGroup-Komponente und wie ich den Eingabestatus innerhalb der FormGroup verwende, um die Validierungsmeldung (falls vorhanden) anzuzeigen:
components/UI/FormGroup.js
import { useState } from 'react'; Eingabe aus './Input' importieren; import './FormGroup.scss'; Funktion FormGroup(props) { const [message, setMessage] = useState(null); const [isInputValid, setIsInputValid] = useState(false); let className = 'form-group'; if(props.className) { className = `form-group ${props.className}`; } let labelCmp = ( ); if(props.sideLabelElement) { labelCmp = ({labelCmp} {props.sideLabelElement}); } Funktion isInputValidOrMessageChangeHandler(changedIsInputValid, changesMessage) { setIsInputValid(changedIsInputValid); setMessage(changedMessage); } zurückkehren ({labelCmp}); } Standard-FormGroup exportieren;{!isInputValid && {message}
}
Wie Sie dem obigen Code entnehmen können, habe ich die Zustände message
definiert, um die aktualisierte message
zu speichern isInputValid
code> Der von der Eingabekomponente übergebene Status. Ich habe in der Eingabekomponente zwei Zustände definiert, um diese Werte zu speichern, aber ich muss in dieser Komponente zwei weitere Zustände definieren, um die aktualisierten und übergebenen Werte in der Eingabekomponente zu speichern. Das ist etwas seltsam und scheint mir nicht die beste Art zu sein, es zu tun.
Die Frage ist: Ich denke, ich kann React Context (useContext) oder React Redux verwenden, um das Propellerbohrproblem hier zu lösen. Ich bin mir jedoch nicht sicher, ob meine aktuelle Statusverwaltung schlecht ist und mithilfe von React Context oder React Redux verbessert werden kann. Denn soweit ich weiß, kann React Context in Situationen, in denen sich der Status häufig ändert, schrecklich sein, aber wenn der Kontext anwendungsweit verwendet wird, funktioniert dies. Hier kann ich einen Kontext erstellen, um das gesamte Formular zu speichern und zu aktualisieren und so eine formularweite Erweiterung zu ermöglichen. React Redux hingegen passt möglicherweise nicht am besten zum Silo und ist möglicherweise etwas übertrieben. Was denken Sie? Was könnte in dieser besonderen Situation eine bessere Alternative sein?
Hinweis: Da ich neu bei React bin, bin ich offen für alle Ihre Vorschläge zu meiner gesamten Codierung, von einfachen Fehlern bis hin zu allgemeinen Fehlern. Danke!
关于 React 状态管理有两种主要思想流派:受控和非受控。受控表单可能会使用 React 上下文进行控制,其中可以在任何地方访问值以提供反应性。但是,受控输入可能会导致性能问题,尤其是在每个输入上更新整个表单时。这就是不受控制的表单出现的地方。通过这种范例,所有状态管理都必须利用浏览器的本机功能来显示状态。这种方法的主要问题是你失去了表单的 React 方面,你需要在提交时手动收集表单数据,并且为此维护多个引用可能很乏味。
受控输入如下所示:
编辑:正如@Arkellys指出的那样,您不一定需要引用来收集表单数据,这是一个使用
的示例FormData
并且不受控制:
从这两个示例中可以明显看出,使用任一方法维护多组件表单都是乏味的,因此,通常使用库来帮助您管理表单。我个人推荐React Hook Form作为经过实战测试、维护良好且易于使用的表单图书馆。它采用不受控制的形式来实现最佳性能,同时仍然允许您观看单个输入以进行反应式渲染。
关于是否使用 Redux、React 上下文或任何其他状态管理系统,假设您正确实现的话,通常在性能方面没有什么区别。如果您喜欢flux 架构,那么请务必使用 Redux,但在大多数情况下,React 上下文是既高性能又足够。
您的
useInput
自定义挂钩看起来是解决问题react-hook-form
和react-final-form
的勇敢但误导性的尝试代码>已经解决了。您正在创建不必要的复杂性和不可预测的副作用有了这个抽象。此外,您镜像 props a> 这通常是 React 中的反模式。如果您确实想实现自己的表单逻辑(我建议您不要这样做,除非是出于教育目的),您可以遵循以下准则:
useMemo
和useRef
尽可能少地重新渲染这是我用来在 Redux 等发布-订阅库和通过组件树传播状态之间做出决定的一个直接方面。
如果两个组件具有父子关系并且彼此距离最多两条边,则将子状态传播到父级
父级 -> child1-level1 -> child1-level2 ------ 好
父级 -> child1-level1 ------ 好
父级 -> child1-level1 -> child1-level2 -> child1-level3 --> 行程过多,无法将状态从 child1-level3 更改为父级
自实施以来