乐趣区

关于react.js:当useEffect遇到函数依赖

上面的代码实现了一个繁难的登录性能(为了缩小代码量,去掉了明码)。

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

怎么解决?
利用 useEffectcleanup
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;
退出移动版