上面的代码实现了一个繁难的登录性能(为了缩小代码量,去掉了明码)。
import React, { useState } from 'react';const api = { login(username) { console.log('username', username); },};function Login() { const [username, setUsername] = useState(''); const onSubmit = () => { if (!username) { alert('Please input username'); return; } api.login(username,); }; return ( <div> <h1>Login</h1> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} /> <button onClick={onSubmit}>submit</button> </div> );}export default Login;
当初咱们要减少一个需要:
监听页面上的回车事件,而后发动登录,代码如下:
useEffect(() => { const onKeyup = (e) => { if (e.key === 'Enter') { onSubmit(); } } window.addEventListener('keyup', onKeyup);}, []);
代码看起来没问题,但运行一下,咱们就会发现并没有依照预期倒退,在输入框曾经输出用户名的前提下,页面上还是会始终提醒让咱们输出用户名。
什么起因?
问题出在onKeyup
外部的onSubmit
,因为onSubmit
所在的useEffect
没有依赖,所以只会在初始化执行一次,onSubmit
外部的username
也就会始终处于首次渲染的状态,值为空字符。
如何解决?
将onSubmit
退出useEffect
的依赖中。
useEffect(() => { const onKeyup = (e) => { if (e.key === 'Enter') { onSubmit(); } } window.addEventListener('keyup', onKeyup);}, [onSubmit]);
依赖加好了,然而看着下面的代码,咱们又会发现一个问题,因为react
会在每次渲染的时候从新创立组件内非hooks
的值,这就导致了每次渲染的onSubmit
的值产生扭转,进而导致监听onSubmit
的useEffect反复执行。
如何解决?
给onSubmit
加上useCallback
,将username
退出到useCallback
的依赖中。
const onSubmit = useCallback(() => { if (!username) { alert('Please input username'); return; } login(username);}, [username]);
这样,在每次组件渲染时,只有当username
的值产生扭转,onSubmit
的值才会跟着产生扭转,从而解决了useEffect
重复执行的问题。
咱们再运行一下,又有新问题了!
当咱们输出的username
长度超过1
时,会发现login
接口被调用了屡次。
什么起因?
问题来到了useCallbak
,每次username
发生变化的时候,onSubmit
都会被更新,从而导致useEffect
为每次username
的扭转都执行了一次,也就屡次绑定了onKeyup
了,后果是屡次调用onSubmit
。
怎么解决?
利用useEffect
的cleanup
。
在cleanup
外面解绑onKeyup
。这样就不会存在屡次绑定的onKeyup
被执行的状况了,只会存在一个最新的onKeyup
绑定。
useEffect(() => { const onKeyup = (e) => { if (e.key === 'Enter') { onSubmit(); } } window.addEventListener('keyup', onKeyup); return () => { window.removeEventListener('keyup', onKeyup); }}, [onSubmit]);
至此,代码工作的完整。
残缺的代码如下:
import React, { useState, useEffect, useCallback } from 'react';const login = (username) => { console.log('username', username);};function Login() { const [username, setUsername] = useState(''); const onSubmit = useCallback(() => { if (!username) { alert('Please input username'); return; } login(username); }, [username]); useEffect(() => { const onKeyup = (e) => { if (e.key === 'Enter') { onSubmit(); } } window.addEventListener('keyup', onKeyup); return () => { window.removeEventListener('keyup', onKeyup); } }, [onSubmit]); return ( <div> <h1>Login</h1> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} /> <button onClick={onSubmit}>submit</button> </div> );}export default Login;