The question:
I use the useSelect
hook to retrieve data from the database when developing for the block editor.
Browsing the Gutenberg code, I see that in addition to the function parameter, useSelect
has an optional second parameter that accepts an array.
Here is an example:
const { media, mediaUpload } = useSelect(
( select ) => ( {
media:
id === undefined
? undefined
: select( coreStore ).getMedia( id ),
mediaUpload: select( blockEditorStore ).getSettings().mediaUpload,
} ),
[ id ]
);
What’s the use of [ id ]
in this example?
In other case, an empty array is passed. Here is an example:
const { postId, isEditedPostNew, hasRemoteAutosave } = useSelect(
( select ) => ( {
postId: select( 'core/editor' ).getCurrentPostId(),
isEditedPostNew: select( 'core/editor' ).isEditedPostNew(),
getEditedPostAttribute: select( 'core/editor' )
.getEditedPostAttribute,
hasRemoteAutosave: !! select( 'core/editor' ).getEditorSettings()
.autosave,
} ),
[]
);
What’s the use of []
in this example?
The Solutions:
Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.
Method 1
Short Answer
The second parameter for useSelect
is the second parameter for useCallback
, so check the React website and you’d understand what the heck that second parameter is.. 🙂
Long Answer
useSelect
is a custom React hook which uses the core useCallback
hook in React which returns a memoized callback which can avoid expensive calculations on every render, and if the second parameter for useSelect
is provided, then as documented: ( deps
is the second parameter, whereas mapSelect
is the first parameter )
deps
Array
: If provided, this memoizes themapSelect
so the samemapSelect
is invoked on every state change unless the dependencies
change.
And by “memoizes the mapSelect
“, it means that useSelect
will call useCallback
like so: useCallback( mapSelect, deps )
, and thus the second parameter for useSelect
is actually the second parameter for useCallback
, and:
-
If
deps
is an empty array ([]
),mapSelect
will be memoized and never be recreated. -
If
deps
is a non-empty array (e.g.[ fooState ]
),mapSelect
will also be memoized, but recreated only if the dependency (fooState
) is changed. -
If
deps
is not an array,mapSelect
will not be memoized, hence it will always be recreated.
So that means, as long as an array is provided, the mapSelect
will be memoized (or you can say that it’s being cached) so that on the next and all other renderings of the component, the same callback will be used instead of being created:
// Although these callbacks use the same code, they're not exactly the same.
const callback = ( a, b ) => a + b;
const callback2 = ( a, b ) => a + b;
console.log( callback === callback2 ); // false
// Same as above; these are not exactly the same.
const callback3 = function ( msg ) { alert( msg ); };
const callback4 = function ( msg ) { alert( msg ); };
console.log( callback3 === callback4 ); // false
// So because of that, the following callback will be created on the initial
// render and then recreated on the next renders.
const onClick = () => console.log( 'clicked' );
// Therefore, if your callback is "heavy", then you can memoize your callback
// using the useCallback hook. And for example, the following callback will be
// created only on the initial render and then it's memoized so that on the
// next renders, the callback is not recreated and instead, the memoized one
// is used.
const onClick2 = useCallback( () => console.log( 'clicked' ), [] );
PS: The above onClick
callbacks are just for demonstration purposes. In actual implementation, you wouldn’t want to memoize such very basic code… because in that case, the optimization would cost more than not having it.
Note about the empty array ([]
) vs non-empty one (e.g. [id]
)
So React stated that:
The array of dependencies is not passed as arguments to the callback.
Conceptually, though, that’s what they represent: every value
referenced inside the callback should also appear in the dependencies
array.
Therefore, in the first example in the question, id
is added as a dependency because it is referenced inside the callback, i.e. select( coreStore ).getMedia( id )
, and if id
isn’t in the dependencies array, then the id
value will always be the initial/current value by the time useSelect
is called, i.e. before the callback is memoized.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0