React:如果 useCallback returns 一个值可以吗,或者这是一个糟糕的模式?
React: Is it okay if useCallback returns a value, or is this a bad pattern?
我有一个名为 filterContactsByValue
的函数。它是柯里化的,接受一个值和一个联系人列表,然后根据该值和returns(新)过滤列表过滤列表。
由于列表通常很大(超过 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
);
根据 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>
);
}
简答:使用 useMemo
而不是 useCallback
,像这样:
const filteredContacts = useMemo(() => filterContacts(contacts), [
value
]);
...
<ContactList contacts={filteredContacts} />
为什么? useCallback
记住函数的创建。意思是,如果差异参数相同,函数的引用将是相同的。每次仍然会调用它,并且在您的情况下,不会阻止任何计算。
您想要的是仅在 value
更改时过滤您的联系人。 useMemo
会记住函数的最后一个 return 值,并且只会在 diffing 参数更改时重新 运行。而且它们不会每 800 毫秒改变一次以上,因为你很好地去抖动了它。
PS:您可以使用 useCallback
来防止 filterContacts
像这样无缘无故地重新计算:
const filterContacts = useCallback(() => filterContactsByValue(value.toLowerCase(), [value]);
即使在您的情况下,性能提升也很小。
我有一个名为 filterContactsByValue
的函数。它是柯里化的,接受一个值和一个联系人列表,然后根据该值和returns(新)过滤列表过滤列表。
由于列表通常很大(超过 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
);
根据 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>
);
}
简答:使用 useMemo
而不是 useCallback
,像这样:
const filteredContacts = useMemo(() => filterContacts(contacts), [
value
]);
...
<ContactList contacts={filteredContacts} />
为什么? useCallback
记住函数的创建。意思是,如果差异参数相同,函数的引用将是相同的。每次仍然会调用它,并且在您的情况下,不会阻止任何计算。
您想要的是仅在 value
更改时过滤您的联系人。 useMemo
会记住函数的最后一个 return 值,并且只会在 diffing 参数更改时重新 运行。而且它们不会每 800 毫秒改变一次以上,因为你很好地去抖动了它。
PS:您可以使用 useCallback
来防止 filterContacts
像这样无缘无故地重新计算:
const filterContacts = useCallback(() => filterContactsByValue(value.toLowerCase(), [value]);
即使在您的情况下,性能提升也很小。