React-Form输入时组件持续重渲染、输入内容自动重置问题如何修复?
问题修复方案
问题根因
- 你在组件的同步渲染逻辑中直接调用了
setPayload,每次组件渲染时只要props.payload不为空就会触发state更新,进而触发无限重渲染,导致输入框内容被重置 - 你将
LoginWindow定义为Auth组件内部的子组件,每次Auth重渲染时都会生成一个全新的LoginWindow组件实例,原Modal会被直接卸载再挂载,输入的内容自然会被清空 - 输入框作为受控组件没有绑定
value属性与state关联,重渲染后输入内容无法和state同步 - 未做缓存的对象、函数每次渲染都会生成新的引用,也会加重不必要的重渲染
修复步骤
- 移除渲染逻辑中的同步
setPayload调用,改用useEffect监听props.payload变化后再更新本地state - 不要在组件内部定义子组件,直接把Modal的JSX写在Auth的return中,或者将
LoginWindow抽到组件外部,通过props传递依赖参数 - 给受控输入框加上
value属性绑定对应的state - 对需要作为依赖传递的对象、函数用
useMemo、useCallback做缓存,避免不必要的重渲染
修复后参考代码
import React, {useState, useEffect, useCallback, useMemo} from 'react'; import useApi, {IPayload} from './hooks/useApi'; import Modal from 'react-modal' import './modal.css'; Modal.setAppElement('#root') interface IProps { payload:IPayload|null, closeAction?:()=>void, } const defaultProps:IProps = { payload:null, closeAction: ()=>null, } function Auth(props:IProps):JSX.Element{ const [auth, updateAuth] = useState<boolean>(false) const [authModalIsOpen, setAuthModal] = useState(true) const [username, setUsername] = useState('') const [password, setPassword] = useState('') // 用useCallback缓存函数,避免每次渲染生成新引用 const forceLogin = useCallback(()=>{ setAuthModal(true) updateAuth(false) }, []) const closeAuthModal = useCallback(()=>{ if(auth){ setAuthModal(false) } }, [auth]) const loginAction = useCallback((res:any)=>{ updateAuth(true) setAuthModal(false) setUsername(res.auth) }, []) // 用useMemo缓存对象,避免每次渲染生成新引用 const opts = useMemo(()=>({ username: username, password: password, fail: forceLogin, }), [username, password, forceLogin]) const defaultPayload = useMemo(():IPayload => ({ path:'notes/validateAuth/', method: 'GET', body: null, callback: loginAction }), [loginAction]) const [payload, setPayload] = useState(defaultPayload) // 改用useEffect监听props.payload变化,避免渲染期间同步更新state useEffect(()=>{ if(props.payload!==null){ setPayload(props.payload) } }, [props.payload]) const SubmitLogin = useCallback(()=>{ setPayload(defaultPayload) }, [defaultPayload]) // 直接返回Modal JSX,不要在内部定义子组件避免重复卸载挂载 return( <Modal isOpen={authModalIsOpen} onRequestClose={closeAuthModal} className='SearchModal' overlayClassName='SearchOverlay' > <div className="IoLinks_wrapper"> <div className="IoLinks_incoming"> <form onSubmit={(e)=>{ e.preventDefault() SubmitLogin() }}> {/* 给输入框加上value属性绑定state */} <input name='username' className='greenput_narrow' value={username} onChange={(e)=>setUsername(e.target.value)} /><br /> <input type="password" name='password' className='greenput_narrow' value={password} onChange={(e)=>setPassword(e.target.value)} /><br /> <button className='greenput_narrow' type="submit">Login</button> </form> </div> <div className="IoLinks_outgoing"> <h1>NullDisk</h1> <ul> <li>Military Grade Encryption</li> <li>Zettelkasten Schema</li> <li>VIM Keybindings</li> </ul> </div> </div> </Modal> ) } Auth.defaultProps = defaultProps export default Auth;
内容的提问来源于stack exchange,提问作者Raven
相关产品推荐
相关产品推荐

