React 输入中的 Onchange 导致输入在每个字母上重新呈现

Posted

技术标签:

【中文标题】React 输入中的 Onchange 导致输入在每个字母上重新呈现【英文标题】:Onchange in React input causes the input to rerender on every letter 【发布时间】:2021-12-03 22:21:48 【问题描述】:

所以我在两个函数中创建了输入,并在Onchange 上获取了输入到它们中的值。但是每当我输入输入时,我一次只能输入一个字母,然后才会失去焦点,我相信这是因为组件一直在渲染。 我还意识到,每当我删除 Onchange 和 value 道具时,一切都会恢复正常,我可以轻松地输入所有内容。我该如何解决这个问题?

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";


function 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>
    );
  

  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?&nbsp;
                <AuthType goToMenu="Signup">click here</AuthType>
              </div>
            </div>
          </div>
        </div>
      </CSSTransition>
    );
  


  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" />
            <Form label="Email" type="email"/>
            <Form label="Date of Birth" type="date" />
            <Form label="Password" type="password"/>
            <Form label="Confirm Password" type="password" />
            <div className=style.button>
              <input type="submit" value="Sign Up" />
            </div>
            <div className=style.text>
              have an&nbsp;
              <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;

【问题讨论】:

【参考方案1】:

可能重复:In React ES6, why does the input field lose focus after typing a character?

一种解决方案是将电子邮件表单移到父组件之外。

const Emailform = ( onChange ) => 
  return (
    <div>
      <label>Email:</label>
      <form>
        <input
          type="email"
          name="email"
          required="true"
          onChange=onChange
        />
      </form>
    </div>
  );
;

const App = () => 
  const [email, setEmail] = useState("");
  return (
    <div>
        <Emailform onChange=(e) => setEmail(e.target.value) />
    </div>
  );
;

【讨论】:

仍然没有变化...而且更糟糕的是,我现在无法输入任何内容... 在不将电子邮件/值传递给 Emailform 的情况下试一试。我已将其从示例中删除。 这也不行……【参考方案2】:

不要将您的组件定义放在其他组件中。如果这样做,您会在每次渲染时创建一个新组件。

您需要设法将 Emailform、Passform 移到 Auth 之外。

结帐this sandbox 进行实验

function Auth() 
  // ? the bad place to define components
  const Emailform = () => 
    ...
  
  // ? the bad place to define components
  const Passform = () => 
    ...
  

【讨论】:

以上是关于React 输入中的 Onchange 导致输入在每个字母上重新呈现的主要内容,如果未能解决你的问题,请参考以下文章

react input 输入中文拼音和onChange事件的交互

React 输入设置状态 onChange 设置多个状态属性

React-onChange后输入字段变为字符串

具有数百个输入的巨大 React 状态数组,状态变化缓慢 onChange

在 React、Redux-Form 和 GraphQL 中选择输入中的 onChange 事件从数据库中查询

React-输入事件