Creating NewsGrid, My Bespoke Multi-Column List Component

Kevin Haube
4 min readMar 15, 2020

I’m in the middle of coding a front-end prototype, and I had a need for a grid of news articles on the home page to display recent articles and videos to members after they logged in. Naturally, being the Material-UI shill I am, I looked into their <GridList> component. This would’ve been serviceable if it weren’t for my desire to have varying heights for each <NewsItem>. Because the Material-UI <GridList> is row-based, but my articles varied in height (not width), I was getting a white space at the bottom of every row that had uneven article heights. Enter: <NewsGrid>

White space appearing at the bottom of both rows due to row-based <GridList>

Rendering Columns

Before we dive into how each row gets the proper data, I want to introduce our rendering method that allows for a variable number of columns. The <NewsGrid> renders columns based on the props.cols property. Coming from Python, I was a little confused that JavaScript didn’t have a function similar to range(x, y) that created a list (read: Array) of numbers between your X and including your Y value. Instead of a calling a simple

cols = range(0, props.cols)

I had to create the for loop pictured below to fill the cols[] array that would later be used to render the proper amount of columns using the .map() function.

As you can see, we return a row of columns that double as items and containers, so they behave accordingly. Currently, I’m developing this as a desktop version, so I set the lg property of each column to 12 divided by the number of columns since Material-UI measures 1 full screen as 12 units. This way, the columns always fill the designated container. To make it more dynamic, I can set the xs prop to that value, and change the number of columns dynamically with screen width. If you’re creating something similar, I’d recommend that approach!

Sorting Columns & Rows

I had to create a function that could be called each time a column’s data needed to be created, and would only grab the appropriate set of data from a JSON array sent from the back-end. If you approach this linearly you get some issues. For example, say we have a 3 column grid and 9 posts in our JSON array. If we wanted to figure out which posts belonged in row 2, we could have a conditional like this:

if (itemIndex % 2 === 0) { … } //Put item in column 2

With this logic, operating linearly in our columns (i.e. starting at column 1 and iterating to the 3rd), you’d run into an issue where article 6 would end up in column 2, instead of the third column where it belonged. For this reason, I decremented from the last column to the first, removing any possibility of wrongly sorted data.

createColumn() and removePostsFromData() functions

For the decrementing creation of each column’s data, from n to 1, to work properly, I wanted to remove each post after it was pushed to the columnPosts array. When you just delete an object from the JSON array, you end up with something like this:

arr = [1, 2, undefined, 4, 5, undefined, 7…]

When you splice out a single object, though, it’ll shift all remaining elements after it down and fully delete the index. That way, when I run the dataset through the createColumnData() function again with a decremented column index, it won’t find an undefined index and cause an error.

Filling the Columns

The final step is to sandwiching everything together in the decrementing loop to build the column data. This part was super simple, so here’s a code snippet.

Commented out is the test code that was later refactored into a simple decrementing loop

I did want to leave in my test code to show that there is a good bit of refactoring I did after I got my MVP up and running. Once I refactored to the decrementing loop the whole component became way more dynamic than before. The columnData variable should look familiar. In my first snippet, it’s what’s filling a column when it’s created.

The Result

I’m pretty satisfied with the result, especially how dynamic it is! The columns load the right data, and though I’m loading dummy data, for now, there will eventually be an API returning JSON data in this format that this will plugin seamlessly with. I didn’t cover too much about the <NewsItem> component, but the data being passed into the <NewsGrid> is an array of those <NewsItem> components.

I won’t be making the code public for now, other than the snippets shared in this write-up, but keep your eyes peeled for future projects where I use it for column-based rendering with row-based sorting!

--

--

Kevin Haube

Double Bachelor’s of Science Scholar, and hobbyist writer at the intersection of Psychology and AI.