Reputation: 433
I am developing a multi-page website using Next.js for the frontend and Strapi for the backend. On the blog page, the data is dynamically fetched from the database. I am trying to display this data in an adaptive layout using Masonry.
The problem I am having is that on first load, the page displays every grid-item in a single vertical column. On reload, the Masonry layout then takes effect, but with some overlapping items. On a second reload, everything then displays properly and is even responsive. However, if I navigate away from the page and come back, it is back to square one.
Here is the code for the blog page :
import React from 'react';
import Layout from '../components/StaticLayout';
import Link from 'next/link';
import fetch from 'isomorphic-unfetch';
import '../../static/styles.min.css';
let Masonry = '';
export default class Blog extends React.Component {
constructor(props) {
super(props);
this.state = {
appIsMounted: false
}
}
static async getInitialProps() {
const res = await fetch('http://localhost:1337/blogposts');
const data = await res.json();
return {
posts: data
};
}
async componentDidMount() {
let masonry = await import('masonry-layout');
this.setState({
appIsMounted: true
})
}
render() {
return (
<Layout>
<h1 className="blogHeader">Blog</h1>
{this.state.appIsMounted ? (
<div className="grid js-masonry" data-masonry-options='{ "itemSelector": ".grid-item", "columnWidth": 400 }'>
{this.props.posts.map(
post => (
<div key={post.id} className="grid-item">
<Link href="/p/[id]" as={`/p/${post.id}`}>
<img src={`http://localhost:1337${post.Image.url}`} alt={post.Image.name} className="postImage" />
</Link>
<div className="text">
<Link href="/p/[id]" as={`/p/${post.id}`}>
<a className="postLink">{post.Title}</a>
</Link>
<p className="blurb">{post.Content.substring(0, 300) + '...'}</p>
<Link href="/p/[id]" as={`/p/${post.id}`}>
<button className="viewPost">Read More!</button>
</Link>
</div>
</div>
)
)}
</div>
) : null}
</Layout>
)
}
}
To be noted, the StaticLayout script only ensure the header is at the top of each page.
EDIT
So I've solved the overlapping part of the problem. Instead of initializing Masonry in the html, I've added a js file to './static'. On my blog page, I've added the following :
import Head from 'next/head'
...
return(
<Head>
<script type="text/javascript" src="/static/runMasonry.js"></script>
</Head>
...
)
And the runMasonry.js file looks like this :
window.onload = function () {
var elem = document.querySelector('.grid');
var msnry = new Masonry(elem, {
// options
itemSelector: '.grid-item',
columnWidth: 160,
gutter: 20
});
}
But when I first arrive on the page or navigate away and come back, the elements are still displayed in a column and require a reload.
Upvotes: 5
Views: 4653
Reputation: 433
Came up with a non-ideal fix, but it works.
Added the following code to the runMasonry.js file :
window.addEventListener("DOMSubtreeModified", function () {
var elem = document.querySelector('.grid');
if (elem) {
var msnry = new Masonry(elem, {
// options
itemSelector: '.grid-item',
columnWidth: 160,
gutter: 20
});
}
});
So it detects when new page content is loaded and, if it finds my .grid element, re-runs masonry.
Leaving this open in case someone can provide a better, long-term solution.
Upvotes: 2