javascriptreactjsjsxwordpress-gutenberggutenberg-blocks

React component that groups posts and displays them in sets of 2 or 3, alternating between the two configurations


I'm building a Gutenberg block that that groups posts and displays them in sets of 2 or 3, alternating between the two configurations. This is the code I came up (some code omitted for brevity):

const { posts, hasPostsResolved } = useSelect(select => {
    const query = {
        order: 'asc',
        orderby: 'title',
        status: 'publish',
        per_page: 10,
    };
    return {
        posts: select('core').getEntityRecords('postType', 'post', query),
        hasPostsResolved: select('core').hasFinishedResolution('getEntityRecords', ['postType', 'post', query]),
    }
}, []);

const displayPosts = (posts) => {

    var posts_per_slide = 3;
    return (
        <>
            {posts.map((post, index) => {
                if (index % posts_per_slide === 0) {
                    posts_per_slide = (posts_per_slide === 3) ? 2 : 3;
                    return (
                        <div key={index / posts_per_slide} className={`post-group posts-${posts_per_slide}`}>
                            {posts.slice(index, index + posts_per_slide).map((groupedPost, groupIndex) => (
                                <article
                                    id={`post-${groupedPost.id}`}
                                    className="wp-block-posts-carousel__post"
                                    key={index + groupIndex}
                                >
                                    <div className="wp-block-posts-carousel__post-thumbnail">
                                        <img src="https://placehold.co/1000x700" width="1000" height="700" />
                                        <h2 className="wp-block-posts-carousel__post-title">
                                            {groupedPost.title.rendered
                                                    ? decodeEntities(groupedPost.title.rendered)
                                                : __('(no title)')}
                                        </h2>
                                    </div>
                                </article>
                            ))}
                        </div>
                    );
                }
                return null;
            })}
        </>
    );

};

return (
    <>
        <div {...blockProps}>
            {!hasPostsResolved &&
                <Spinner />
            }
            {hasPostsResolved && posts?.length === 0 &&
                <p>
                    {__('No posts found')}
                </p>
            }
            {hasPostsResolved && posts?.length > 0 &&
                <>
                    <div class="wp-block-posts-carousel__posts">
                        {displayPosts(posts)}
                    </div>
                </>
            }
        </div>
    </>
);

But it's not working as expected, because some of the posts are repeated in multiple posts groups, and also, I need the first group to contain three posts, not two.

Any help would be appreciated.


Solution

  • I managed to find a solution by grouping the posts in arrays of 3 and 2 BEFORE rendering them:

    const displayPosts = (posts) => {
    
        // Group posts in sets of three and two alternately.
        var groupedPosts = [];
        for (let i = 0; i < posts.length; i += 5) {
            groupedPosts.push(posts.slice(i, i + 3));
            groupedPosts.push(posts.slice(i + 3, i + 5));
        }
    
        return (
            <>
                {groupedPosts.map((group, index) => (
                    <div key={index} className={`post-group posts-${group.length}`}>
                        {group.map((post, subIndex) => (
                            <article key={subIndex} id={`post-${post.id}`} className="wp-block-jn-post-carousel__post">
                                <div className="wp-block-jn-post-carousel__post-thumbnail">
                                    <img src="https://placehold.co/1000x700" width="1000" height="700" />
                                    <h2 className="wp-block-jn-post-carousel__post-title">
                                        {post.title.rendered
                                            ? decodeEntities(post.title.rendered)
                                            : __('(no title)')}
                                    </h2>
                                </div>
                            </article>
                        ))}
                    </div>
                ))}
            </>
        );
    
    };