React: Это нормально, если useCallback возвращает значение, или это плохой шаблон? - PullRequest
0 голосов
/ 12 марта 2019

У меня есть функция с именем filterContactsByValue. Он каррируется и принимает значение и список контактов, а затем фильтрует список на основе значения и возвращает (новый) отфильтрованный список.

Поскольку этот список часто велик (более 10.000 записей), веб-приложение должно работать на смартфонах, а фильтр учитывает множество значений, поэтому я хочу оптимизировать вычислительные ресурсы. Поэтому я использую useDebounce, чтобы не вычислять без необходимости.

Я также использовал useCallback вот так, чтобы запомнить вычисление filteredContacts:

function FilteredContacts({contacts}) {
  const [filterParam, setFilterParam] = useState('');
  const [value] = useDebounce(filterParam, 800);
  const filterContacts = filterContactsByValue(value.toLowerCase());

  // Is this okay? ? ...
  const getFilteredContacts = useCallback(() => filterContacts(contacts), [
    value
  ]);

  return (
    <div className="main">
      <SearchBar
        value={filterParam}
        onChangeText={setFilterParam}
      />
      // ... and then this? ?
      <ContactList contacts={getFilteredContacts()} />
    </div>
  );
}

Мне было интересно, нормально ли это, или возвращать такие значения - плохая практика. Если это плохо, почему и как бы вы улучшили его?

Edit: Функция filterContactsByValue:

import { any, filter, includes, map, pick, pipe, toLower, values } from 'ramda';
import { stringFields } from './config/constants';

const contactIncludesValue = value =>
  pipe(
    pick(stringFields),
    map(toLower),
    values,
    any(includes(value))
  );

const filterContactsByValue = pipe(
  contactIncludesValue,
  filter
);

Ответы [ 2 ]

2 голосов
/ 12 марта 2019

Согласно https://github.com/xnimorz/use-debounce у вас уже есть useDebouncedCallback крючок.

const getFilteredContacts = useDebouncedCallback(
    () => filterContactsByValue(value.toLowerCase()),
    800,
    [value]
  );

Вы также можете использовать команду lodash debounce или throttle (когда у вас есть lodash в вашем проекте), но, как упоминал @skyboyer, вы можете закончить с устаревшими версиями обратного вызова, которые будут запущены после соответствующей задержки

export {debounce} from 'lodash'; 

const getFilteredContacts = useCallback(
    debounce(() => filterContactsByValue(value.toLowerCase()), 1000),
    [value]
);

но useMemo будет лучшим вариантом, потому что вы действительно не хотите выполнения функции в вашем методе рендеринга

const FilteredContacts = ({contacts}) => {
    const [filterParam, setFilterParam] = useState('');
    const [value] = useDebounce(filterParam, 800);
    const contactsFilter = useMemo(
        () => filterContactsByValue(value.toLowerCase()),
        [value]
    );
    const filteredContacts = useMemo(
        () => contactsFilter(contacts), 
        [value, contacts]
    );

    return (
        <div className="main">
            <SearchBar
                value={filterParam}
                onChangeText={setFilterParam}
            />
            <ContactList contacts={filteredContacts} />
        </div>
    );
}
1 голос
/ 12 марта 2019

Краткий ответ: используйте useMemo вместо useCallback, вот так:

const filteredContacts = useMemo(() => filterContacts(contacts), [
    value
  ]);

...
<ContactList contacts={filteredContacts} />

Почему?useCallback запоминает создание функции.Это означает, что ссылка на функцию будет такой же, если параметры сравнения одинаковы.Он будет по-прежнему вызываться каждый раз, и в вашем случае это не помешает никаким вычислениям.

Вам нужно только отфильтровать контакты, если value изменится.useMemo запоминает последнее возвращаемое значение вашей функции и будет перезапускаться только при изменении параметров сравнения.И они не будут меняться чаще, чем раз в 800 мс, потому что вы хорошо отсеиваете его.

PS: вы можете использовать useCallback, чтобы предотвратить повторное вычисление filterContacts без всякой причины, например:

 const filterContacts = useCallback(() => filterContactsByValue(value.toLowerCase(), [value]);

Даже в этом случае прирост производительности незначителен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...