【问题标题】:React: Is it okay if useCallback returns a value, or is this a bad pattern?React:如果 useCallback 返回一个值可以吗,还是这是一个不好的模式?
【发布时间】:2019-08-02 08:18:30
【问题描述】:

我有一个名为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>
  );
}

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

编辑: 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


    【解决方案1】:

    简答:使用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]);
    

    即使在您的情况下,性能提升也很小。

    【讨论】:

      【解决方案2】:

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

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

      您也可以使用 lodash 的 debounce 或限制(当您的项目中有 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>
          );
      }
      

      【讨论】:

      • 顺便说一句,据我所知useDebouncedCallback 更好,因为如果回调被更改(基于deps),它会清除最后一个计时器ID。因此,虽然使用 lodash + useCallback 可能会以过时的回调版本结束,但会在适当的延迟后运行。
      • const [filterParam, setFilterParam] = useState(''); const getFilteredContacts = useDebouncedCallback( () =&gt; filterContactsByValue(filterParam.toLowerCase())(contacts), 800, [filterParam] ); 不起作用,它会抛出错误:TypeError: Object(...) is not a function
      • @fard 我在问题中添加了咖喱版本。
      • @J.Hesters 告诉我它现在是否有效 :) 像 useDebouncedMemo 这样的东西将是最干净的解决方案
      猜你喜欢
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-21
      • 2014-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多