Update number of posts when attribute changed

I have a simple block that shows the most recent posts. But I want the number of posts to be changeable. To do that, I created a simple attribute and integrate it with a range control.

const onChangePosts = ( newContent ) = {
    setAttributes( { numberPosts: newContent } );
};

RangeControl
    value={ attributes.numberPosts }
    onChange={ ( value ) = onChangePosts( value ) }
    min={ 1 }
    max={ 10 }
/

And here I'm getting the posts:

const posts = useSelect( ( select ) = {
    return select( 'core' ).getEntityRecords(
        'postType',
        'post',
        {
            'per_page': attributes.numberPosts
        }
    );
}, [] );

But when I update the attribute, the number of posts rendered in the block don't change.

The is the complete Edit function:

export default function Edit( { attributes, setAttributes  } ) {
    const posts = useSelect( ( select ) = {
        return select( 'core' ).getEntityRecords(
            'postType',
            'post',
            {
                'per_page': attributes.numberPosts
            }
        );
    }, [] );

    const onChangePosts = ( newContent ) = {
        setAttributes( { numberPosts: newContent } );
    };

    return (
        div { ...useBlockProps() }
            InspectorControls key=setting
                Panel
                    PanelBody title=Number of posts
                        RangeControl
                            value={ attributes.numberPosts }
                            onChange={ ( value ) = onChangePosts( value ) }
                            min={ 1 }
                            max={ 10 }
                        /
                    /PanelBody
                /Panel
            /InspectorControls
            { ! posts  'Loading' }
            { posts  posts.length === 0  'No Posts' }
            ul
            { posts  posts.length  0  posts.map(post = (
                li
                    a href={ post.link }
                        { post.title.rendered }
                    /a
                /li
            ))}
            /ul
        /div
    );
}

index.js file:

registerBlockType( 'create-block/dynamic-block', {
    /**
     * @see ./edit.js
     */
    edit: Edit,
} );

block.json file:

{
    $schema: https://schemas.wp.org/trunk/block.json,
    apiVersion: 2,
    name: create-block/dynamic-block,
    version: 0.1.0,
    title: Dynamic Block,
    category: widgets,
    icon: smiley,
    description: Example static block scaffolded with Create Block tool.,
    supports: {
        html: false
    },
    textdomain: dynamic-block,
    editorScript: file:./index.js,
    editorStyle: file:./index.css,
    style: file:./style-index.css,
    attributes: {
        numberPosts : {
            type: integer,
            default: 3
        }
    }
}

Plugin init file:

function create_block_dynamic_block_block_init() {
    register_block_type( __DIR__ . '/build', [
        'render_callback' = gutenberg_examples_dynamic_render_callback,
    ]);
}
add_action( 'init', 'create_block_dynamic_block_block_init' );

function gutenberg_examples_dynamic_render_callback( $block_attributes, $content ) {
    $recent_posts = wp_get_recent_posts( array(
        'numberposts' = $block_attributes['numberPosts'],
        'post_status' = 'publish',
    ) );

    if ( count( $recent_posts ) === 0 ) {
        return 'No posts';
    }

    $posts = '';
    foreach ($recent_posts as $post) {
        $post_id = $post['ID'];
        $posts .= sprintf(
            'pa class=wp-block-my-plugin-latest-post href=%1$s#%2$s/a/p',
            esc_url( get_permalink( $post_id ) ),
            esc_html( get_the_title( $post_id ) )
        );
    }

    return $posts;
}

Topic block-editor Wordpress

Category Web


when I update the attribute, the number of posts rendered in the block don't change

Yes, and that's because you did not include the numberPosts attribute in the second parameter (which is an array of dependencies) for useSelect(), so the (memoized) value remains unchanged, even when the block's state is changed, or that you selected a new number of posts via the range control.

So your useSelect() should include the above dependency, i.e. use [ attributes.numberPosts ] instead of [] as the 2nd parameter, like so:

const posts = useSelect( ( select ) => {
    return select( 'core' ).getEntityRecords(
        'postType',
        'post',
        {
            'per_page': attributes.numberPosts
        }
    );
}, [ attributes.numberPosts ] ); // here, make sure you include all dependencies

Additionally, you should set a key for your list items, e.g. <li key={ `post-${ post.id }` }>.

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.