(ง •̀_•́)ง Ming's blog

Blog post fetcher with React + hitting GraphQL endpoint

This post provides the building blocks (like Lego) to help you build something similar. You can click here to view the code and here for the demo.

The idea

I have been a user of Matters (a mainly Mandarin blogging platform) for a while now. The user home page design fits about three articles.

user home page

This results in an “infinite scroll hell” homepage for users who has many articles. Their contents will sink to the bottom and never see the sunlight.

Some users started to create a “content page post” with all the articles they have written, but for users with 100 and more articles, doing this by hand is just too big of a hassle.

I see the need for a blog post fetcher, so I build one. The fetcher works like this:

matalogue

Users can enter their username along with a number to fetch the first N posts. Click fetch. Users will then see an editing page that allows them to choose if they want their articles with date, numbered, and in chronological order or not. When they are happy with the editing, they can copy everything by clicking the copy button.

Building block 1: Stepping between steps

step1

My main component contains a header(1), the content div(2), and the footer(3). Inside the content div, there are two children components:

  1. Step1: the page where users enter username and click fetch (image above).
  2. Step2: the editing page (image below).

step2

To display the correct component on each step, I have an if statement checking if the variable currentStep in the state is relevant.

//Return both components
<div id="contentBox" className="content">
  <Step1 
  currentStep = { this.state.currentStep } 
  userInfo = { this.state.userInfo } 
  handleInput = { this.handleInput } 
  grabPosts = { this.grabPosts } 
  />
  <Step2 
  currentStep = { this.state.currentStep } 
  articles = { this.state.articles } 
  edit = { this.state.edit } 
  handleEdit = { this.handleEdit } 
  goBackToStepOne = { this.goBackToStepOne } 
  copyToClipBoard = { this.copyToClipBoard }
  />
</div>

//Check in each  
function Step2(props) {
 if (props.currentStep !== 2) {
  return null;
 }
 ...
}

Building block 2: Updating the state

Input data are stored as two objects in the state. Thus I break them into two functions. The only difference is to use the checked value when it is a checkbox. Otherwise, they both make a copy of the original state object and store the new value back to the state.

 handleEdit(event) {
  const target = event.target;
  const value = target.type === "checkbox" ? target.checked : target.value;
  const name = target.name;
  let newEdit = this.state.edit;
  newEdit[name] = value;
  this.setState({ edit: newEdit });
 }

 handleInput(event) {
  const { name, value } = event.target;
  let newInfo = this.state.userInfo;
  newInfo[name] = value;
  this.setState({ userInfo: newInfo });
 }

Building block 3: Hitting the GraphQL endpoint

The fetch button triggers a function grabPosts, which performs a fetch and updates the state.

const { userName, first } = this.state.userInfo;
fetch(fetchURL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
    query: fetchQuery,
    variables: {
    userName: userName,
    first: parseInt(first),
    },
  },
})
.then(...)
.then((result) => {
  ...
  this.setState({ currentStep: 2, articles: fetchedArticles });
})
.catch((err) => console.error(err));

Building block 4: Editing and copy to clipboard

I passed the data to Step2 as articles and do all the editing before displaying them.

//Apply the edit options
const { startDate, endDate, withNum, withDate, sortDateAsc } = props.edit;
let articles = props.articles.sort((a, b) => (sortDateAsc ? new Date(b.date) - new Date(a.date) : new Date(a.date) - new Date(b.date)));

if (startDate !== "") articles = articles.filter((article) => article.date > startDate);
if (endDate !== "") articles = articles.filter((article) => article.date < endDate);

display = articles.map((a, index) => <Article key={index} link={a.link} title={a.title} date={a.date.slice(0, 10)} no={index + 1} withNum={withNum} withDate={withDate} />);

//To display
<div id="articles" className="articles">{display}</div>

I found this piece of code online to copy everything to the clipboard:

const articles = document.getElementById("articles");
const btnCopy = document.getElementById("btnCopy");
let r = document.createRange();
r.selectNode(articles);
window.getSelection().removeAllRanges();
window.getSelection().addRange(r);
document.execCommand('copy');
window.getSelection().removeAllRanges();

#React #GraphQL