问题
On my journey to try and understand React Hooks better I came across some behaviour I did not expect. I was attempting to create an array of refs and pushing to said array via an onRef function I would pass to my <div>'s
. The array kept getting bigger everytime the component re-rendered presumably just because it was a simple arrow function and not memoized.
So then I added the useCallback
hook to make sure that I wouldn't get the same ref multiple times, but to my surprise it still called the function every re-render. After adding an empty array as second parameter the refs only fired once per component as expected.
This behaviour is demonstrated in the snippet below.
const Example = () => {
const _refs = React.useRef([]);
// Var to force a re-render.
const [ forceCount, forceUpdate ] = React.useState(0);
const onRef = (ref) => {
if (ref && ref !== null) {
console.log("Adding Ref -> Just an arrow function");
_refs.current.push(ref);
}
}
const onRefCallbackWithoutInputs = React.useCallback((ref) => {
if (ref && ref !== null) {
console.log("Adding Ref -> Callback without inputs.");
_refs.current.push(ref);
}
});
const onRefCallbackEmptyArray = React.useCallback((ref) => {
if (ref && ref !== null) {
console.log("Adding Ref -> Callback with empty array");
_refs.current.push(ref);
}
}, []);
React.useEffect(() => {
console.log("Refs size: ", _refs.current.length);
});
return (
<div>
<div ref={onRef}/>
<div ref={onRefCallbackWithoutInputs}/>
<div ref={onRefCallbackEmptyArray}/>
<div onClick={() => forceUpdate(forceCount + 1)}
style = {
{
width: '100px',
height: '100px',
marginTop: '12px',
backgroundColor: 'orange'
}
}>
{'Click me to update'}
</div>
</div>
);
};
ReactDOM.render(<Example/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>
I assumed useCallback
would have an empty array as a default for the second parameter. So what exactly does not giving a second parameter do? Why does it behave differently?
回答1:
For both useMemo
and useCallback
(which is essentially just a special case of useMemo
), if the second argument is an empty array, the value will be memoized once and always returned.
If the second argument is omitted, the value will never be memoized, and the useCallback
and the useMemo
doesn't do anything.
Perhaps there's some edge case where you might conditionally memoize:
useMemo(someValue, shouldMemoize ? [] : null)
But in the vast majority of cases, the second argument to both useMemo
and useCallback
should be considered mandatory. And in fact, the Typescript definitions treat them this way.
// Require a second argument, and it must be an array
function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;
// Second argument can be undefined, but must be explicitly passed as undefined, not omitted.
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
There's an open pull request that's enhancing the exhaustive-deps
hooks eslint rule so that it will raise a lint error if the second argument is omitted, so pretty soon this will likely be a linter error.
回答2:
I think it's the same logic behind all the hooks, useEffect, useLayoutEffect, useCallback, useMemo, for dependency array, if no dependencies passed means we passed the null value for dependencies, hence comparison would always result false and inline function will execute every time.
if Empty dependencies are passed means there is nothing to compare further hence inline function will only execute once.( it is just like we are instructing React for no further comparison )
if the array are passed with some variable then it will compute the inline function based on the changes in the variable.
Though instance of the inline function will always created.
来源:https://stackoverflow.com/questions/55026139/whats-the-difference-between-usecallback-with-an-empty-array-as-inputs-and-u