React输入框绑定onChange后每输入一个字符就重渲染失焦如何解决
问题修复方案
核心原因
你把Emailform、Passform、AuthType、Login、Signup这些子组件直接定义在了Auth组件内部,每次输入内容触发setEmail/setPassword更新Auth组件状态时,这些内部子组件都会被重新创建,React会判定为全新组件,直接卸载旧实例挂载新实例,自然就会导致输入框失焦,只能输入单个字符。
另外你还有两个隐性问题:
useClickOutside钩子的useEffect没有加依赖数组,每次组件渲染都会重复绑定、解绑事件监听器- 自定义
Form组件内读取属性错误,你传递的是type属性,组件内错误读取了props.input
修复步骤
- 将所有定义在
Auth组件内部的子组件,全部移到Auth组件外部,组件需要用到的状态、方法通过props传递 - 给
useClickOutside的useEffect添加空依赖数组,保证事件只在组件挂载时绑定一次 - 修正
Form组件的属性读取逻辑 - 移除输入框外不必要的
form标签,避免触发默认提交刷新页面的行为
修改后的代码示例
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"; // 移到Auth外部的子组件 const Emailform = ({email, setEmail}) => { return ( <div className={style.formBox}> <label className={style.label}>邮箱:</label> <div className={style.input}> <input type="email" name="email" required value={email} onChange={(e) => setEmail(e.target.value)} /> </div> </div> ); } const Passform = ({password, setPassword}) => { return ( <div className={style.formBox}> <label className={style.label}>密码:</label> <div className={style.input}> <input type="password" name="password" required value={password} onChange={(e) => setPassword(e.target.value)} /> </div> </div> ); } const AuthType = ({goToMenu, setActiveMenu, link, children}) => { return ( <a href={link || '#'} className={style.menuItem} onClick={(e) => { e.preventDefault() goToMenu && setActiveMenu(goToMenu) }} > {children} </a> ); } const Login = ({activeMenu, domNode, email, setEmail, password, setPassword, setActiveMenu, signInWithEmailAndPassword, signInWithGoogle}) => { return ( <CSSTransition in={activeMenu === "main"} unmountOnExit timeout={500}> <div ref={domNode}> <div className={style.login}> <h1 className={style.title}>Clip It</h1> <Emailform email={email} setEmail={setEmail}/> <Passform password={password} setPassword={setPassword}/> <div className={style.button}> <button type="button" onClick={() => signInWithEmailAndPassword(email, password)} > 登录 </button> <button type="button" onClick={signInWithGoogle} > 谷歌账号登录 </button> </div> <div className={style.text}> <p className={style.plink}>忘记密码</p> <div> 没有账号? <AuthType goToMenu="Signup" setActiveMenu={setActiveMenu}>点击注册</AuthType> </div> </div> </div> </div> </CSSTransition> ); } const Signup = ({activeMenu, domNode, setActiveMenu}) => { return ( <CSSTransition in={activeMenu === "Signup"} unmountOnExit timeout={500}> <div ref={domNode}> <div className={style.signUp}> <div className={style.title}> Clip It</div> <Form label="名字" type="text" /> <Form label="姓氏" type="text" /> <Form label="邮箱" type="email"/> <Form label="出生日期" type="date" /> <Form label="密码" type="password"/> <Form label="确认密码" type="password" /> <div className={style.button}> <button type="button">注册</button> </div> <div className={style.text}> 已有账号? <AuthType goToMenu="main" setActiveMenu={setActiveMenu}>立即登录</AuthType> </div> </div> </div> </CSSTransition> ); } const Form = (props) => { return ( <div className={style.formBox}> <label className={style.label}>{props.label}:</label> <div className={style.input}> <input type={props.type} name={props.type} required /> </div> </div> ); } // 修复useClickOutside的依赖 const 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 Auth() { const [activeMenu, setActiveMenu] = useState("main"); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [user, loading, error] = useAuthState(auth); const domNode = useClickOutside(() => { setActiveMenu("main"); }); return ( <div className={style.container}> <Login activeMenu={activeMenu} domNode={domNode} email={email} setEmail={setEmail} password={password} setPassword={setPassword} setActiveMenu={setActiveMenu} signInWithEmailAndPassword={signInWithEmailAndPassword} signInWithGoogle={signInWithGoogle} /> <Signup activeMenu={activeMenu} domNode={domNode} setActiveMenu={setActiveMenu} /> </div> ); } export default Auth;
内容的提问来源于stack exchange,提问作者jotarotaroo
相关产品推荐
相关产品推荐

