React函数组件开发中非受控组件的传值及重复渲染问题如何解决
问题解决方法
首先纠正一个实现偏差:你当前的Input组件属于受控组件,并非你预期的非受控组件,非受控组件不会通过内部state绑定原生input的value属性。
问题1:输入值无法完整传递给Form组件
原因
React的setState是异步批量更新的,调用setValue(e.target.value)后,当前函数闭包内保存的value还是更新前的旧值,直接传给onChange就会出现滞后一位的问题。
解决方法
直接将最新的输入值传给父组件回调即可,无需依赖更新前的state:
// Input组件内修改handleChange function handleChange(e) { const newValue = e.target.value; setValue(newValue); onChange(newValue); }
问题2:输入时两个Input同步重渲染
原因
- Form组件的
email/password状态更新时,所有子组件默认都会跟随重渲染 - 你传给Input的
onChange回调是Form每次渲染时新生成的函数,即使给Input加了缓存也会因为props变化导致缓存失效
解决方法
步骤1:给Input组件加渲染缓存
用React.memo包裹Input组件,让它仅在props发生变化时才触发重渲染:
// Input组件修改 import React, { useState, memo } from "react"; const Input = memo(({ type, placeholder, onChange }) => { const [value, setValue] = useState(""); function handleChange(e) { const newValue = e.target.value; setValue(newValue); onChange(newValue); } return ( <> {console.log("render", placeholder)} <input type={type} placeholder={placeholder} value={value} onChange={handleChange} /> </> ); });
步骤2:稳定Form内回调函数的引用
用useCallback包裹两个onChange回调,保证依赖不变的情况下函数引用不会更新,避免触发Input的重渲染:
// Form组件修改 import React, { useState, useCallback } from "react"; import Input from "./Input"; const Form = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [state, setState] = useState(0); function handleSubmit(e) { e.preventDefault(); console.log(email, password); } // 用useCallback包裹回调 const handleEmail = useCallback((value) => { setEmail(value); console.log(value); }, []); const handlePassword = useCallback((value) => { setPassword(value); }, []); function handleReset(e) { setEmail(""); setPassword(""); setState(state + 1); } return ( <form onSubmit={handleSubmit}> <Input key={state + "email"} type="email" placeholder="이메일" onChange={handleEmail} /> <Input key={state + "password"} type="password" placeholder="비밀번호" onChange={handlePassword} /> <button type="submit">가입</button> <button type="button" onClick={handleReset}> 초기화 </button> </form> ); };
可选:实现真正的非受控组件
如果你确实需要非受控的实现,可以去掉Input内部的state,改用ref操作原生输入框,减少内部state带来的渲染开销,也不需要靠修改key卸载重建组件来实现重置:
// 非受控版Input组件 import React, { useRef, memo, useEffect } from "react"; const Input = memo(({ type, placeholder, onChange, resetFlag }) => { const inputRef = useRef(null); // 响应重置操作清空输入 useEffect(() => { inputRef.current.value = ""; }, [resetFlag]); function handleChange(e) { onChange(e.target.value); } return ( <> {console.log("render", placeholder)} <input ref={inputRef} type={type} placeholder={placeholder} onChange={handleChange} /> </> ); }); export default Input;
内容的提问来源于stack exchange,提问作者Aaaaaa
相关产品推荐
相关产品推荐

