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

72 Views Asked by At

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.

1

There are 1 best solutions below

0
leemon On BEST ANSWER

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>
            ))}
        </>
    );

};