关于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;

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理