Hello!
On posts, I have a meta field that allows the author to select up to 2 related posts on the WordPress side. On the Frontity theme side, I take those IDs and utilize it to create clickable thumbnails for those posts.
On average it works fine, but when the post is older, or not in the same category, it doesn’t load it properly. I’ve utilized the libraries.source.api and libraries.source.populate to get the post by ID, but I still run into 2 main issues:
- the thumbnail doesn’t populate into the source
- the title and excerpt are fetched and output fine, but when the link is clicked, the site crashes
Here’s the Post component:
import React, { useEffect, useState } from 'react';
import { connect, styled, Head } from 'frontity';
import SubscribeBox from '../newsletter/SubscribeBox';
import PostFooter from './PostFooter';
import MoreArticles from './MoreArticles';
import mq from '../MediaQueries';
import Link from "@frontity/components/link";
import ShareLinks from '../snippets/ShareLink';
const Post = ({ state, libraries, actions }) => {
//Get info about the current URL
const data = state.source.get(state.router.link);
//Get the current post's data
const post = state.source[data.type][data.id];
//get the featured Image ID
const featuredImgId = post.featured_media;
//get the featured image from attachments
const featuredImgData = state.source.attachment[featuredImgId];
//the ability to hide the featured image (binary)
const hideFeaturedImage = post.meta_box.hide_post_featured_image;
//get the author data
const author = state.source.author[post.author];
// Get a human readable date.
const date = new Date(post.date);
//check if the post is sponsored
const sponsored = post.meta_box.sponsored;
// const sponsoredCompany = post.meta_box.company_sponsor;
const sponsoredURL = post.meta_box.sponsored_url;
//get category info
const categoryID = post.categories[0];
const categoryName = state.source.category[categoryID].name;
const categoryLink = state.source.category[categoryID].link;
//get the html2react component that allows us to output html
const Html2React = libraries.html2react.Component;
const catData = state.source.get(categoryLink);
//get the related posts IDs (up to 2 are selectable)
const selectedPostIds = post.meta_box.post_relatedPosts;
const [loadedSelectedPosts, setLoadedSelectedPosts] = useState([]);
/**
* Once the post has loaded in the DOM, prefetch both the
* home posts and the category component so if the user visits
* the home page or the category page, everything is ready and it loads instantly.
* Also fetch the selectedPosts (if there are any) so people can see those are the bottom
* of the article
*/
useEffect(() => {
actions.source.fetch("/");
actions.source.fetch(categoryLink);
const loadSelectedPosts = async (selectedPostIds ) => {
for(const selectedId of selectedPostIds) {
if(loadedSelectedPosts.length < selectedPostIds.length){
// Fetch the post data from the endpoint
const response = await libraries.source.api.get({
endpoint: `/wp/v2/posts/${selectedId}`,
});
const postsAdded = await libraries.source.populate({ response, state }).then(
res => {
actions.source.fetch(res[0].link);
setLoadedSelectedPosts(
[...loadedSelectedPosts, res[0]]
);
}
);
}
}
}
loadSelectedPosts(selectedPostIds);
}, [loadedSelectedPosts.length] );
return(
<>
{ (!post.yoast_head_json.description && post.excerpt.rendered ) &&
<Head>
<meta name="description" content={post.excerpt.rendered.replace(/<[^>]*>?/gm, '')} />
</Head>
}
<Article>
<div className="container">
<div className="row">
<div className="col-lg-11 offset-lg-1">
<div className="row">
<div className="col-lg-11">
{categoryName === "Digging In" &&
<DiggingIn link={categoryLink}>Digging In</DiggingIn>
}
{ (sponsored && sponsoredURL ) &&
<Sponsored link={sponsoredURL} target="_blank">Sponsored</Sponsored>
}
<H1>
<Html2React html={ post.title.rendered } />
</H1>
</div>
<div className="col-lg-9">
<LeadParagraph>
<Html2React html={ post.excerpt.rendered } />
{/* <RevealText text={post.excerpt.rendered} delay="0.2" /> */}
</LeadParagraph>
{ (featuredImgData && hideFeaturedImage !== "1") &&
<FeaturedImg src={ featuredImgData.source_url } alt={featuredImgData.alt_text} />
}
{featuredImgData &&
<Caption><Html2React html={ featuredImgData.caption.rendered } /></Caption>
}
</div>
<ArticleBody className="col-lg-8 col-md-10 order-1 order-lg-0">
<Html2React html={ post.content.rendered } />
</ArticleBody>
<Aside className="col-xl-3 col-lg-4 pl-lg-5 pl-xl-3 offset-xl-1 order-0 order-lg-1">
<PostInfo>
<Link link={author.link}><AuthorImg src={author.avatar_urls['96']} alt={author.name} /></Link>
<div>
<AuthorLink link={author.link}>by {author.name}</AuthorLink>
<DateWrapper>{date.toLocaleDateString('en-ca', { year: 'numeric', month: 'long', day: 'numeric' } )}</DateWrapper>
in <Link link={categoryLink}>{categoryName}</Link>
</div>
</PostInfo>
<StyledSubscribeBox
className="dark-form dark-form--mobile"
headerText="Become smarter in just 5 minutes"
header="h3"
blurbText="Get the 5 minute weekly newsletter for all the latest in the Canadian construction industry."
buttonText="Subscribe"
formWidth="100%"
location="post-sidebar"
/>
</Aside>
</div>
<ShareLinks />
<PostFooter />
</div>
<MoreArticles when={ catData.isReady && ( loadedSelectedPosts.length === selectedPostIds.length ) } />
</div>
</div>
</Article>
</>
);
}
export default connect(Post);
Here’s the MoreArticles component (located inside the Post Component, at the bottom):
import React from 'react';
import { styled, connect } from 'frontity';
import { IoFlashSharp } from 'react-icons/io5';
import ArticleList from './lists/ArticleList';
import ArticleNumberedTextList from './lists/ArticleNumberedTextList';
const MoreArticles = ({ state }) => {
const data = state.source.get(state.router.link);
const postId = data.id;
//Get the current post's data
const post = state.source[data.type][postId];
//get the category information
let categoryID = post.categories[0];
let categoryName = state.source.category[categoryID].name;
let categoryLink = state.source.category[categoryID].link;
let catData = state.source.get(categoryLink);
const catPosts = catData.items;
//set base of suggested posts as most recent
let morePosts = [...catPosts];
//get any hand picked suggested posts
const selectedPostIds = post.meta_box.post_relatedPosts;
let selectedPosts = [];
if(morePosts && selectedPostIds){
selectedPostIds.map((selectedPostId) => {
const selectedPost = state.source.post[selectedPostId];
const selectedPostObj = (selectedPost) && {type: 'post', id: selectedPostId, link: selectedPost.link};
selectedPosts.push( selectedPostObj );
})
}
if(morePosts && selectedPosts){
morePosts.unshift(...selectedPosts);
}
let postCount = 0;
const filteredCatPosts = ( morePosts && post.id ) ?
morePosts.filter(post => post.id !== postId ).filter(post => {
postCount++;
if(!selectedPostIds.includes(post.id) && postCount > 2){
return post;
}else if(postCount <= 2){
return post;
}
})
: [];
return(
<>
{ filteredCatPosts &&
<MoreArticlesContainer className="col-12">
<div className="row">
<div className="col-lg-8">
<h3><IoFlashSharp />You might also like</h3>
<ArticleList articles={filteredCatPosts.slice(0, 2)} />
</div>
<div className="col-lg-4">
<H3>More from {categoryName}</H3>
<ArticleNumberedTextList posts={filteredCatPosts.slice(2, 7)} />
</div>
</div>
</MoreArticlesContainer>
}
</>
);
}
export default connect(MoreArticles);
Really appreciate any help on this - please let me know if you need to see any more info or code or anything. I’m still learning React in general so appreciate any pointers at all - thank you!