useSelect second parameter

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 the mapSelect so the same mapSelect 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

Leave a Comment