开发者问题收集

React hook 缺少依赖

2019-09-17
495

我希望有人能向我解释在这种情况下 React hook 的正确用法,因为我似乎找不到解决方法。

以下是我的代码

  useEffect(() => {
    _getUsers()
  }, [page, perPage, order, type])

  // This is a trick so that the debounce doesn't run on initial page load
  //  we use a ref, and set it to true, then set it to false after
  const firstUpdate = React.useRef(true);
  const UserSearchTimer = React.useRef()
  useEffect(() => {
    if(firstUpdate.current)
      firstUpdate.current = false;
    else 
      _debounceSearch()
  }, [search])

  function _debounceSearch() {
    clearTimeout(UserSearchTimer.current);
    UserSearchTimer.current = setTimeout( async () => {
        _getUsers();
    }, DEBOUNCE_TIMER);
  }

  async function _getUsers(query = {}) {
    if(type) query.type = type;
    if(search) query.search = search;

    if(order.orderBy && order.order) {
      query.orderBy = order.orderBy;
      query.order = order.order;
    }

    query.page = page+1;
    query.perPage = perPage;

    setLoading(true);
    try {
      await get(query);
    }
    catch(error) {
      console.log(error);
      props.onError(error);
    }
    setLoading(false);
  }

所以本质上我有一个显示用户的表,当页面发生变化、每个页面、顺序或类型发生变化时,我想重新查询我的用户列表,因此我有一个 useEffect 用于这种情况。

现在一般我会将 _getUsers() 函数放入该 useEffect 中,但唯一的问题是我还有另一个 useEffect 用于当我的用户开始在搜索框中搜索时。

我不想使用用户在框中输入的每一个字母重新查询我的用户列表,而是想使用在用户停止输入后触发的防抖动器。

所以很自然地我会创建一个 useEffect,它会监视值搜索,每次搜索发生变化时,我都会调用我的 _debounceSearch 函数。

现在我的问题是我似乎无法摆脱 React 依赖警告,因为我的第一个 useEffect 依赖项中缺少 _getUsers 函数,该函数被我的 _debounceSearch fn 使用,而在我的第二个 useEffect 中,我的第二个 useEffect 依赖项中缺少 _debounceSearch。

我怎样才能以“正确”的方式重写它,这样我就不会最终收到有关缺少依赖项的 React 警告?

提前致谢!

2个回答

我将设置一个状态变量来保存去抖动的搜索字符串,并使用它获取用户。

假设您的组件将查询参数作为 props,它将是这样的:

function Component({page, perPage, order, type, search}) {
    const [debouncedSearch, setDebouncedSearch] = useState(search);

    const debounceTimer = useRef(null);
    // debounce
    useEffect(() => {
        if(debounceTime.current) {
            clearTimeout(UserSearchTimer.current);
        }
        debounceTime.current = setTimeout(() => setDebouncedSearch(search), DEBOUNCE_DELAY);
    }, [search]);

    // fetch 
    useEffect(() => {
        async function _getUsers(query = {}) {
            if(type) query.type = type;
            if(debouncedSearch) query.search = debouncedSearch;

            if(order.orderBy && order.order) {
              query.orderBy = order.orderBy;
              query.order = order.order;
            }

            query.page = page+1;
            query.perPage = perPage;

            setLoading(true);
            try {
              await get(query);
            }
            catch(error) {
              console.log(error);
              props.onError(error);
            }
            setLoading(false);
        }
        _getUsers();
    }, [page, perPage, order, type, debouncedSearch]);
}

在初始渲染时,去抖动效果将设置一个去抖动计时器……但没关系。
去抖动延迟后,它将设置 deboucedSearch 状态为相同值。
由于 deboucedSearch 没有改变,ferch 效果将不会运行,因此不会浪费获取。

随后,在更改除搜索之外的任何查询参数时,获取效果将立即运行。 在更改搜索参数时,获取效果将在去抖动后运行。

但理想情况下,去抖动应该在搜索参数的 <input /> 处完成。 在获取组件中进行去抖动的小问题是,搜索中的每个更改都将经过去抖动,即使它是通过文本框输入以外的方式发生的,例如单击预配置搜索的链接。

ckedar
2019-09-17

围绕钩子依赖关系的规则非常简单明了:如果钩子函数使用或引用组件范围内的任何变量,则应考虑将其添加到依赖项列表中( https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies )。

对于您的代码,您应该注意以下几点:

1.使用第一个 _getUsers useEffect:

useEffect(() => {
    _getUsers()
  }, [page, perPage, order, type])

 // Correctly it should be:
 useEffect(() => {
    _getUsers()
  }, [_getUsers])

此外,您的 _getUsers 函数当前在每次重新渲染组件时都会重新创建,你可以考虑使用 React.useCallback 来记忆它。

2.第二个 useEffect

useEffect(() => {
    if(firstUpdate.current)
      firstUpdate.current = false;
    else 
      _debounceSearch()
  }, [search])

// Correctly it should be
useEffect(() => {
    if(firstUpdate.current)
      firstUpdate.current = false;
    else 
      _debounceSearch()
  }, [firstUpdate, _debounceSearch])
Thai Duong Tran
2019-09-17