React:如果useCallback返回一个值,或者这是一个糟糕的模式,它可以吗?

问题描述 投票:0回答:2

我有一个名为filterContactsByValue的函数。它是curry并接收值和联系人列表,然后根据值过滤列表并返回(新)过滤列表。

由于列表通常很大(10.000+条目),因此Web应用程序应该在智能手机上运行,​​并且过滤器会考虑许多值,我想优化计算资源。因此我使用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>
  );
}

我想知道这是否可以,或者返回这样的值是不好的做法。如果不好,为什么以及如何改进它?

编辑: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
);
reactjs react-hooks react-component
2个回答
2
投票

根据https://github.com/xnimorz/use-debounce你已经有useDebouncedCallback钩。

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

你也可以使用lodash的去抖或节流(当你在项目中使用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
投票

简短回答:使用useMemo而不是useCallback,如下所示:

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

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

为什么? useCallback记住了函数的创建。意思是,如果diffing参数相同,函数的引用将是相同的。它每次都会被调用,在你的情况下,不会阻止任何计算。

如果value发生变化,你想要的只是过滤你的联系人。 useMemo会记住函数的最后一个返回值,并且只会在diffing参数更改时重新运行。并且它们每800毫秒不会改变超过一次,因为你很好地去除了它。

PS:您可以使用useCallback来防止filterContacts无缘无故重新计算:

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

即便如此,性能提升也很小。

© www.soinside.com 2019 - 2024. All rights reserved.