0

I tried using useSelect in event callback as described in block editor handbook, however, I get undefined since value is retrieved later than I need it. I guess I need some kind of extra async function to catch value when is retrieved, or is there some other mechanism not described here?

Note that I am not using useSelect in a classical way with callback because value can't be retrieved upon component refresh, but on user action

This code is supposed to check, when certain type of post is selected then retreive it's featured image, otherwise let user select image. Currently I am only logging to the console because I am debugging it.

SingleImage is component abstracted away, which lets user select and replace image.

import { useBlockProps, URLInput } from '@wordpress/block-editor';
import SingleImage from '../../components/singleImage.jsx'
import { useSelect } from '@wordpress/data';

import './editor.scss';

export default function Edit({ attributes, setAttributes }) {
    const { getEntityRecord } = useSelect('core');
    const urlInputSetter = (url, post) => {
        if (post && ! attributes.imgID) {
            if (post.type === 'virtualne-izlozbe') {
                const record = getEntityRecord('postType', 'virtualne-izlozbe', post.id );
                if (record)
                    console.log(record.featured_media);
                else
                    console.log('empty');
            }
        }
        setAttributes( { link: url, postTitle: (post && post.title) } )
    }

    return (
        <div { ...useBlockProps() }>
            <URLInput
                value={ attributes.link }
                onChange={ urlInputSetter }
                label="Link na objavu"
                className="post-link"
            />
            { attributes.postTitle && <div className="post-title">Slika vodi na objavu { attributes.postTitle }</div> }
            <SingleImage
                imgID={attributes.imgID}
                imgURL={attributes.imgURL}
                alt="Slika koja vodi na objavu"
                setImgIdAndUrl={(imgID, imgURL) => setAttributes({imgID, imgURL})}
                title="Izaberi sliku"
                help="Slika koja vodi na objavu"
            />

        </div>
    );
}

1 Answer 1

0

In the documentation you posted it says:

Don’t use useSelect this way when calling the selectors in the render function because your component won’t re-render on a data change.

import { useSelect } from '@wordpress/data';
 
function Paste( { children } ) {
    const { getSettings } = useSelect( 'my-shop' );
    function onPaste() {
        // Do something with the settings.
        const settings = getSettings();
    }
    return <div onPaste={ onPaste }>{ children }</div>;
}

Which is basically how you used and that is why your component is re-rendering when there is a change.

You can use react hooks (useState, useEffect)to resolve this issue. See the code below. I have commented the code to explain the changes. (code is not tested)

import { useBlockProps, URLInput } from '@wordpress/block-editor';
import SingleImage from '../../components/singleImage.jsx';
import { useSelect } from '@wordpress/data';
const { useState, useEffect } = wp.element;
import './editor.scss';

export default function Edit({ attributes, setAttributes }) {
    // Prepare state
    const [post, setPost] = useState();

    // useSelect with a callback, notice the third paramter
    const record = useSelect(
        (select) => {
            return select('core').getEntityRecords(
                'postType',
                'virtualne-izlozbe',
                post.id
            );
        },
        // This is a dependency array, everytime the state of
        // "post" changes, useSelect callback will be called.
        [post]
    );

    // Perfom actions on state change
    useEffect(() => {
        // record value change, do someting
        if (record) {
            console.log(record.featured_media);
        } else {
            console.log('empty');
        }
        // Dependency array, every time record changes the useEffect callback
        // will be called
    }, [record]);

    const urlInputSetter = (url, urlPost) => {
        if (urlPost && !attributes.imgID) {
            if (urlPost.type === 'virtualne-izlozbe') {
                // Change state so the useSelect callback will be triggered
                setPost(urlPost);
            }
        }
        setAttributes({ link: url, postTitle: post && post.title });
    };

    return (
        <div {...useBlockProps()}>
            <URLInput
                value={attributes.link}
                onChange={urlInputSetter}
                label="Link na objavu"
                className="post-link"
            />
            {attributes.postTitle && (
                <div className="post-title">
                    Slika vodi na objavu {attributes.postTitle}
                </div>
            )}
            <SingleImage
                imgID={attributes.imgID}
                imgURL={attributes.imgURL}
                alt="Slika koja vodi na objavu"
                setImgIdAndUrl={(imgID, imgURL) =>
                    setAttributes({ imgID, imgURL })
                }
                title="Izaberi sliku"
                help="Slika koja vodi na objavu"
            />
        </div>
    );
}
1
  • No, that is not true. I do not use getEntityRecord in render function, but in UrlInputSetter callback. And that is exactly how this is supposed to work from the handbook: When data is only used in an event callback, the data should not be retrieved on render, so it may be useful to get the selectors function instead.
    – Lovor
    Dec 4, 2021 at 23:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.