Передайте аргумент функции обратного вызова - PullRequest
1 голос
/ 06 мая 2020

Я пытаюсь использовать debounce в loda sh, чтобы отложить onChange, см. Код ниже.

import React, { useState, useEffect, useCallback } from "react";
import { TopBar } from "@shopify/polaris";
import { debounce } from "lodash";

function SearchBar() {
  const [searchValue, setSearchValue] = useState("");

  const handleSearchFieldChange = ((value:string) => {
    setSearchValue(value);
  });

  const debounceLoadData = useCallback(debounce({searchValue} => fetchData, 1000), []);

  useEffect(() => {
    debounceLoadData();
    console.log({searchValue})
  }, [searchValue]);

  function fetchData(value:string) {
    console.log("searchValue " + value);
  }

  const searchFieldMarkup = (
    <TopBar.SearchField
      onChange={handleSearchFieldChange}
      value={searchValue}
      placeholder="Search Value"
    />
  );

  return <TopBar searchField={searchFieldMarkup} />;
}

Вначале я пытался использовать searchValue в функции fetchData, но, похоже, из-за области видимости он не смог его прочитать, он всегда был пустым, хотя состояние было обновлено.

В результате я пытаюсь передать его из debounceLoadData, но я не знаю, как я могу это сделать, так как то, что используется useCallback, - это вызов функции. Как передать searchValue в fetchData внутрь debounce.

Ответы [ 3 ]

3 голосов
/ 06 мая 2020

loda sh debounce принимает функцию в качестве первого аргумента. Вы можете просто использовать fetchData в качестве функции и передать searchValue в debounceLoadData, который затем будет передан в fetchData при вызове

const debounceLoadData = useCallback(debounce(fetchData, 1000), []);

  useEffect(() => {
    debounceLoadData(searchValue);
    console.log({searchValue})
  }, [searchValue]);

debounce фактически возвращает функцию, подумайте о debounce как реализовано, например,

function debounce(func, wait) {
  let timeout
  return function(...args) {
    const context = this
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(context, args), wait)
  }
}

Таким образом, в основном debounceLoadData является возвращаемой функцией здесь, а arguments passed to it i.e ...args затем передаются исходной функции fetchData, например func.apply(context, args)

Также debounceLoadData является создается только один раз, поскольку зависимость обратного вызова - [], вы передадите его в useEffect в качестве зависимости not не будет иметь никакого значения. Как исправить отсутствие предупреждения о зависимости при использовании useEffect React Hook?

1 голос
/ 06 мая 2020

Мне никогда не нравится useCallback, это довольно запутанный крючок, и я всегда использую вместо него useMemo, поскольку он полностью покрывает то, что может useCallback (но не наоборот).

function SearchBar() {
  const [searchValue, setSearchValue] = useState("");

  const handleSearchFieldChange = ((value:string) => {
    setSearchValue(value);
  });

  const debounceLoadData = useMemo(() => debounce(fetchData, 1000), []);
  /**
   * the equivalent of useCallback should be:
   *
   * const debounceLoadData = useCallback(debounce(fetchData, 1000), []);
   * 
   * But I really advice against it!
   * There's unnecessary function invocation compared to useMemo.
   */


  useEffect(() => {
    debounceLoadData(searchValue);  // <- you should pass in arg
    console.log({searchValue})
  }, [searchValue]);

  // ...
}

Тем не менее, для вашего случая я не думаю, что использование loda sh debounce - лучшее решение.

Существует скрытый риск того, что окончательный вызов эффекта fetchData произойдет ПОСЛЕ размонтирования вашего компонента. И если fetchData содержит некоторый лог c мутации состояния, это вызовет ошибку «Невозможно вызвать setState (или forceUpdate) на размонтированном компоненте». что не деструктивно, но и не оптимально.

Я предлагаю вручную отменить вызов, используя setTimeout/clearTimeout. Это довольно просто:

useEffect(() => {
  const timeoutId = setTimeout(() => fetchData(searchValue), 1000)
  return () => clearTimeout(timeoutId)
}, [searchValue])
0 голосов
/ 06 мая 2020

Думаю, вы запутались в функциональном синтаксисе setState. Попробуйте это:

const debounceLoadData = useCallback(() => debounce(() => fetchData(searchValue), 1000), []);
...