我创建了两个带有输入表单的自定义组件,我只是希望能够在其中输入内容,但每当我输入一个字母时,我就会失去焦点,必须一次又一次地单击输入框才能一一输入。我怀疑发生这种情况是因为我在可能重新渲染的输入上放置了 Onchange 道具。每当有变化时整个应用程序。但是这个 Onchange 是我的应用程序运行所需要的,所以现在我陷入了困境。任何帮助表示赞赏
App.js
import style from "./auth.module.css";
import { useEffect, useRef, useState } from "react";
import { useAuthState } from 'react-firebase-hooks/auth';
import { auth, signInWithEmailAndPassword, signInWithGoogle, registerWithEmailAndPassword } from "../../firebase";
import { CSSTransition } from "react-transition-group";
const Auth = () => {
const [activeMenu, setActiveMenu] = useState("main");
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [user, loading, error] = useAuthState(auth);
const Emailform = () => {
return (
<div className={style.formBox}>
<label className={style.label}>Email:</label>
<form className={style.input}>
<input
type="email"
name="email"
required="true"
value={email}
onChange={(e) => setEmail(e.target.value)}/>
</form>
</div>
);
}
const Passform = () => {
return (
<div className={style.formBox}>
<label className={style.label}>Password:</label>
<form className={style.input}>
<input
type="password"
name="password"
required="true"
value={password}
onChange={(e) => setPassword(e.target.value)} />
</form>
</div>
);
}
const ConfirmPass = () => {
return (
<div className={style.formBox}>
<label className={style.label}>Confirm Password:</label>
<form className={style.input}>
<input
type="password"
name="password"
required="true" />
</form>
</div>
);
}
let domNode = useClickOutside(() => {
setActiveMenu(false);
});
return (
<div className={style.container}>
<Login />
<Signup />
</div>
);
function AuthType(props) {
return (
<a
href={props.link}
className={style.menuItem}
onClick={() => props.goToMenu && setActiveMenu(props.goToMenu)}
>
{props.children}
</a>
);
}
/* Login */
function Login() {
return (
<CSSTransition in={activeMenu === "main"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.login}>
<h1 className={style.title}>Clip It</h1>
{/* Email and Password */}
<Emailform/>
<Passform/>
<div className={style.button}>
<input
type="submit"
value="Login"
onClick={() => signInWithEmailAndPassword(email, password)} />
<input
type="submit"
value="Login with Google"
onClick={signInWithGoogle} />
</div>
<div className={style.text}>
<p className={style.plink}>Forgot Password</p>
<div>
Need an account?
<AuthType goToMenu="Signup">click here</AuthType>
</div>
</div>
</div>
</div>
</CSSTransition>
);
}
/* SignUp/Register */
function Signup() {
return (
<CSSTransition in={activeMenu === "Signup"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.signUp}>
<div className={style.title}> Clip It</div>
<Form label="First Name" type="text" />
<Form label="Last Name" type="Text" />
<Emailform value={email} onChange={(e) => setEmail(e.target.value)}/>
<Form label="Date of Birth" type="date" />
<Passform value={password} onChange={(e) => setPassword(e.target.value)}/>
<ConfirmPass />
<div className={style.button}>
<input
type="submit"
value="Sign Up"
onClick={registerWithEmailAndPassword} />
</div>
<div className={style.text}>
have an
<AuthType goToMenu="main"> account</AuthType>
</div>
</div>
</div>
</CSSTransition>
);
}
}
let useClickOutside = (handler) => {
let domNode = useRef();
useEffect(() => {
let clickListener = (event) => {
if (domNode.current && !domNode.current.contains(event.target)) {
handler();
}
};
document.addEventListener("mousedown", clickListener);
return () => {
document.removeEventListener("mousedown", clickListener);
};
});
return domNode;
};
function Form(props) {
return (
<div className={style.formBox}>
<label className={style.label}>{props.label}:</label>
<form className={style.input}>
<input
type={props.input}
name={props.input}
required="true" />
</form>
</div>
);
}
export default Auth;
return (
<div className={style.container}>
<Login />
<Signup />
</div>
);
在上面的代码中,将 Login 和 Signup 的全部内容添加到 return 中,而不是创建不同的组件(函数),因为每次内部状态更新时,组件都会重新渲染,导致焦点被移除。它发生得如此之快,以至于看起来只有焦点被禁用,但组件每次都重新渲染。
解决方案:
return (
<div className={style.container}>
<CSSTransition in={activeMenu === "main"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.login}>
<h1 className={style.title}>Clip It</h1>
{/* Email and Password */}
<Emailform/>
<Passform/>
<div className={style.button}>
<input
type="submit"
value="Login"
onClick={() => signInWithEmailAndPassword(email, password)} />
<input
type="submit"
value="Login with Google"
onClick={signInWithGoogle} />
</div>
<div className={style.text}>
<p className={style.plink}>Forgot Password</p>
<div>
Need an account?
<AuthType goToMenu="Signup">click here</AuthType>
</div>
</div>
</div>
</div>
</CSSTransition>
<CSSTransition in={activeMenu === "Signup"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.signUp}>
<div className={style.title}> Clip It</div>
<Form label="First Name" type="text" />
<Form label="Last Name" type="Text" />
<Emailform value={email} onChange={(e) => setEmail(e.target.value)}/>
<Form label="Date of Birth" type="date" />
<Passform value={password} onChange={(e) => setPassword(e.target.value)}/>
<ConfirmPass />
<div className={style.button}>
<input
type="submit"
value="Sign Up"
onClick={registerWithEmailAndPassword} />
</div>
<div className={style.text}>
have an
<AuthType goToMenu="main"> account</AuthType>
</div>
</div>
</div>
</CSSTransition>
</div>
);
您遇到的每次按键后失去输入焦点的问题是由于您的输入组件(EmailForm 和 PasswordForm)是在 Auth 组件内定义的。这会导致它们在每次渲染时重新定义(并因此重新安装),每次状态更新时都会发生这种情况。为了解决这个问题,您可以将这些组件移到 Auth 函数之外,或者使用 React.memo 来防止不必要的重新渲染。
解决方案1:将输入组件移到外部 通过将 EmailForm 和 PasswordForm 移到 Auth 组件之外,它们将不会在每次渲染时重新定义:
import React, { useState } from 'react';
const EmailForm = ({ email, setEmail }) => (
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
);
const PasswordForm = ({ password, setPassword }) => (
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
);
const Auth = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<div>
<EmailForm email={email} setEmail={setEmail} />
<PasswordForm password={password} setPassword={setPassword} />
</div>
);
};
export default Auth;
解决方案2:使用React.memo 如果出于某种原因您希望将组件保留在 Auth 中,您可以使用 React.memo 包装它们。这将阻止它们重新渲染,除非它们的 props 发生变化:
import React, { useState } from 'react';
const EmailForm = React.memo(({ email, setEmail }) => (
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
));
const PasswordForm = React.memo(({ password, setPassword }) => (
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
));
const Auth = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<div>
<EmailForm email={email} setEmail={setEmail} />
<PasswordForm password={password} setPassword={setPassword} />
</div>
);
};
export default Auth;