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

J. *_*ers 5 reactjs react-component react-hooks

我有一个名为filterContactsByValue. 它被柯里化并接受一个值和一个联系人列表,然后根据该值过滤列表并返回(新的)过滤列表。

由于列表通常很大(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>
  );
}
Run Code Online (Sandbox Code Playgroud)

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

编辑: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
);
Run Code Online (Sandbox Code Playgroud)

Bea*_*oot 9

简短的回答:使用useMemo代替useCallback,如下所示:

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

...
<ContactList contacts={filteredContacts} />
Run Code Online (Sandbox Code Playgroud)

为什么?useCallback记住函数的创建。这意味着,如果比较参数相同,则函数的引用将相同。不过,它仍然会每次被调用,并且在您的情况下,不会阻止任何计算。

您想要的是仅在value发生变化时过滤您的联系人。useMemo记住函数的最后一个返回值,并且仅在比较参数更改时才会重新运行。而且它们每 800 毫秒不会改变一次以上,因为你可以很好地消除它。

PS:您可以用来useCallback防止filterContacts无缘无故地重新计算,如下所示:

 const filterContacts = useCallback(() => filterContactsByValue(value.toLowerCase()), [value]);
Run Code Online (Sandbox Code Playgroud)

即使在你的情况下,性能增益也是很小的。


far*_*ard 2

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

const getFilteredContacts = useDebouncedCallback(
    () => filterContactsByValue(value.toLowerCase()),
    800,
    [value]
  );
Run Code Online (Sandbox Code Playgroud)

您还可以使用 lodash 的去抖或节流(当您的项目中有 lodash 时),但正如 @skyboyer 提到的,您可能会以过时的回调版本结束,将在适当的延迟后运行

export {debounce} from 'lodash'; 

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

Run Code Online (Sandbox Code Playgroud)

但这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>
    );
}
Run Code Online (Sandbox Code Playgroud)