0

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(
            '<p><a 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;
}
4
  • Can you show us your registerBlockType() (i.e. block type registration) code?
    – Sally CJ
    Apr 15, 2022 at 10:03
  • @SallyCJ I updated thje code. BTW, the block is using the structure generated by @wordpress/create-block Apr 15, 2022 at 17:46
  • Thanks, but actually, I wanted to see your attributes list, i.e. attributes: { the list here } that's passed to registerBlockType(). And is this actually a dynamic block? If so, what's your full register_block_type() code?
    – Sally CJ
    Apr 15, 2022 at 23:45
  • @SallyCJ I updated the code. The PHP part is working fine. I'm able to get the correct value from numberPosts attribute. The problem is: in the editor, when I change the number of posts, the block is not updated. Apr 17, 2022 at 12:59

1 Answer 1

0

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 }` }>.

1
  • Thanks for the explanation. I tested but unfortunately it's not working. If I add a console.log(attributes.numberPosts); inside const posts, the value doesn't change when I update the number of posts. Apr 20, 2022 at 17:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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