Reagieren Sie auf alternative Unterstützungsbohrungen (umgekehrt, untergeordnetes Element zu übergeordnetem Element), um Formulare zu verarbeiten
P粉419164700
P粉419164700 2023-09-01 19:45:14
0
2
472
<p>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. </p> <p>Also habe ich die Form-Komponente und die FormGroup-Komponente wie folgt auf der Beispiel-Anmeldeseite platziert: </p> <p><em>pages/Login.js</em></p> <pre class="brush:js;toolbar:false;">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 ( <div className="login"> <div className="login__logo"> Go Cup </div> <p className="login__description"> Melden Sie sich bei Ihrem Go Cup-Konto an </p> <Kartenrand> <Form onSubmit={formSubmitHandler}> <FormGroup id="Anmelden" label="Benutzername oder E-Mail-Adresse" inputProps={{ Typ: "Text", Name: "Anmelden", Gültigkeit: (Wert) => { value = value.trim(); if(!value) { return [false, 'Benutzername oder E-Mail-Adresse ist erforderlich.'] } else if(value.length < 3 || value.length > 30) { return [false, 'Benutzername oder E-Mail-Adresse müssen mindestens 3 und maximal 30 Zeichen haben']; } anders { return [true, null]; } }, onValidityChange: loginValidityChangeHandler, onReset: resetLoginInputHandler }} /> <FormGroup id="Passwort" label="Passwort" sideLabelElement={ <Link zu="/password-reset"> Passwort vergessen? </Link> } inputProps={{ Typ: "Passwort", Name: "Passwort", Gültigkeit: (Wert) => { value = value.trim(); if(!value) { return [false, „Passwort ist erforderlich.“] } else if(value.length < 4 || value.length > 1024) { return [false, 'Passwort muss mindestens 4 oder maximal 1024 Zeichen lang sein.']; } anders { return [true, null]; } }, onValidityChange: passwortValidityChangeHandler, onReset: resetPasswordInputHandler }}/> <div className="text-center"> <Button className="w-100" type="submit"> Anmeldung </Button> <span className="login__or"> oder </span> <Button className="w-100" onClick={switchToSignupHandler}> Melden Sie sich an </Button> </div> </Formular> </Karte> </div> ); } Standard-LoginPage exportieren; </pre> <p>Wie Sie im obigen Code sehen können, verwende ich die FormGroup-Komponente und übergebe die Eigenschaften <code>onValidityChange</code>, um <code>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 <code>isValid</code> und <code>reset</code> 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 <code>isLoginValid</code> auf der Anmeldeseite, um den aktualisierten Statuswert <code>isValid</code> 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. </p> <p>Der Status wird innerhalb der Eingabekomponente verwaltet. Ich habe diese Status: </p> <ul> <li><strong>Wert: </strong>Geben Sie den Wert des Elements ein. </li> <li><strong>isInputTouched</strong>: Bestimmt, ob der Benutzer die Eingabe berührt/fokussiert hat, um zu bestimmen, ob gegebenenfalls eine Validierungsfehlermeldung angezeigt werden soll. </li> </ul> <p>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 (<code>isInputValid = isValid || !isInputTouched</code>), um zu entscheiden, ob die Überprüfungsnachricht angezeigt werden soll.</p> <p>Diese Zustände und Werte werden in einem benutzerdefinierten Hook verwaltet, den ich erstellt habe, <code>useInput</code>, wie folgt: </p> <p><em>hooks/use-state.js</em></p> <pre class="brush:js;toolbar:false;">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; </pre> <p>Ich verwende derzeit diesen benutzerdefinierten Hook in Input.js wie folgt: </p> <p><em>components/UI/Input.js</em></p> <pre class="brush:js;toolbar:false;">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 ( <Eingabe {...Requisiten} className={className} Wert={Wert}onChange={inputChangeHandler} onBlur={inputBlurHandler} /> ); } Standardeingabe exportieren; </pre> <p>In der Eingabekomponente verwende ich direkt den Status <code>isInputValid</code>, um die ungültige CSS-Klasse zur Eingabe hinzuzufügen. Ich übergebe aber auch die Funktionen <code>isInputValid</code>, <code>message</code> und <code>reset</code> an die übergeordnete Komponente darin zu verwenden. Um diese Zustände und Funktionen zu übergeben, verwende ich die in props definierten Funktionen <code>onIsInputValidOrMessageChange</code>, <code>onValidityChange</code> (Props-Drilldown, aber Richtung). Kind zu Eltern). </p> <p>Dies ist die Definition der FormGroup-Komponente und wie ich den Eingabestatus innerhalb der FormGroup verwende, um die Validierungsmeldung (falls vorhanden) anzuzeigen: </p> <p><em>components/UI/FormGroup.js</em></p> <pre class="brush:js;toolbar:false;">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 = ( <label htmlFor={props.id}> {props.label} </label> ); if(props.sideLabelElement) { labelCmp = ( <div className="form-label-group"> {labelCmp} {props.sideLabelElement} </div> ); } Funktion isInputValidOrMessageChangeHandler(changedIsInputValid, changesMessage) { setIsInputValid(changedIsInputValid); setMessage(changedMessage); } zurückkehren ( <div className={className}> {labelCmp} <Eingabe id={props.id} onIsInputValidOrMessageChange={isInputValidOrMessageChangeHandler} {...props.inputProps} /> {!isInputValid && <p>{message}</p>} </div> ); } Standard-FormGroup exportieren; </pre> <p>Wie Sie dem obigen Code entnehmen können, habe ich die Zustände <code>message</code> definiert, um die aktualisierte <code>message</code> zu speichern <code>isInputValid</code> 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. </p> <p><strong>Die Frage ist: </strong>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? </p> <p><strong>Hinweis: </strong>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! </p>
P粉419164700
P粉419164700

Antworte allen(2)
P粉627136450

关于 React 状态管理有两种主要思想流派:受控和非受控。受控表单可能会使用 React 上下文进行控制,其中可以在任何地方访问值以提供反应性。但是,受控输入可能会导致性能问题,尤其是在每个输入上更新整个表单时。这就是不受控制的表单出现的地方。通过这种范例,所有状态管理都必须利用浏览器的本机功能来显示状态。这种方法的主要问题是你失去了表单的 React 方面,你需要在提交时手动收集表单数据,并且为此维护多个引用可能很乏味。

受控输入如下所示:

const [name, setName] = useState("");

return <input value={name} onChange={(e) => setName(e.currentTarget.value)} />

编辑:正如@Arkellys指出的那样,您不一定需要引用来收集表单数据,这是一个使用 FormData

的示例

并且不受控制:

const name = useRef(null);
const onSubmit = () => {
    const nameContent = name.current.value;
}
return <input ref={name} defaultValue="" />

从这两个示例中可以明显看出,使用任一方法维护多组件表单都是乏味的,因此,通常使用库来帮助您管理表单。我个人推荐 React Hook Form 作为经过实战测试、维护良好且易于使用的表单图书馆。它采用不受控制的形式来实现最佳性能,同时仍然允许您观看单个输入以进行反应式渲染。

关于是否使用 Redux、React 上下文或任何其他状态管理系统,假设您正确实现的话,通常在性能方面没有什么区别。如果您喜欢 flux 架构,那么请务必使用 Redux,但在大多数情况下,React 上下文是既高性能又足够。

您的 useInput 自定义挂钩看起来是解决问题 react-hook-formreact-final-form 的勇敢但误导性的尝试代码>已经解决了。您正在创建不必要的复杂性和不可预测的副作用 有了这个抽象。此外,您镜像 props a> 这通常是 React 中的反模式。

如果您确实想实现自己的表单逻辑(我建议您不要这样做,除非是出于教育目的),您可以遵循以下准则:

  1. 在最高共同祖先处保留一个事实来源
  2. 避免镜像和复制状态
  3. 使用 useMemouseRef 尽可能少地重新渲染
P粉596191963

这是我用来在 Redux 等发布-订阅库和通过组件树传播状态之间做出决定的一个直接方面。

如果两个组件具有父子关系并且彼此距离最多两条边,则将子状态传播到父级

父级 -> child1-level1 -> child1-level2 ------ 好

父级 -> child1-level1 ------ 好

父级 -> child1-level1 -> child1-level2 -> child1-level3 --> 行程过多,无法将状态从 child1-level3 更改为父级

  • 如果交互组件之间的边距离超过 2 个,则使用 redux
  • 对同级组件使用 redux,即共享父组件且需要彼此通信的子组件(在侧面板中选择一个树项目,在主组件中显示所选项目的详细信息)

自实施以来

  • 我发现 useInput 是一种过度重构,您的输入组件应该足以管理与输入相关的操作,更好地抽象验证等方面
  • 您可以在表单提交时触发所有验证,在这种情况下,您不需要受控输入(在表单的 onSubmit 事件上附加验证)
  • 但是,如果您的表单包含太多字段(比如 >5),并且您想在提交之前验证该字段,您可以使用输入字段的 onBlur 事件,或者使用 onInput 以及 debounce 操作(例如来自 lodash 的 debounce 操作)或像这样实现

function debounce(func, timeout = 300){
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => { func.apply(this, args); }, timeout);
  };
}
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!