This is the first in (hopefully) a series of articles to document the lessons I learned building and maintaining SSR apps.
This article does not cover the pros/cons of SSR, or the pros/cons of not using tooling or frameworks (e.g., Create React App, Next.js, etc).
I fully acknowledge that manually setting up build configs is painful. There is plenty of great tooling out there to abstract away these configs, so you probably don’t even need to touch any of this stuff to build awesome apps.
But for those ready to embrace the pain…
Let’s start with the basics. We will use React, webpack, and Express to build an SSR app that works as…
In Part 1, I presented an approach to decouple the data fetching/management layer from a React component that renders some UI based on the data, which would free us from being locked into any particular data library or framework. Let’s call this Approach A.
Let’s create a custom hook — useSomeData
— that returns the properties someData
, loading
, and error
regardless of the data fetching/management logic. The following are 3 different implementations of useSomeData
.
With Fetch API and component state:
With Redux:
With Apollo GraphQL:
The 3 implementations above are interchangeable without having to modify this UI…
Suppose I use an external service provider to process payments for my E-commerce app, and I need to embed some external SDK code to integrate the payment service into my app.
In this oversimplified example, let’s say the payment service is responsible for checking whether a given payment method (e.g, Apple Pay and Google Pay) is available based on the customer’s device, region, etc. While my “core” UI component PaymentOptions
is responsible for rendering the available payment methods as options. Lastly, I want the flexibility of adding new payment methods in the future (for 📈💰reasons).
I can write it this way. …
I‘m certain you have seen (or written) this common React pattern: (a) render a placeholder/ loader/spinner while some data is fetched via AJAX, then (b) re-render the component based on the data received. Let’s write a functional component leveraging the Fetch API to accomplish this.
Let’s say my app grows, and there are X
components that use the same data fetching logic because… reasons. To avoid spamming the server with data requests, I decide to use Local Storage to cache the data.
OK… does that mean I need to update the data logic X
times? 😬😱
Nope, let’s DRY it up by writing a custom hook useSomeData
. …
Let’s create a button that will:
Here is the markup.
Here are the functions. Let’s also measure the duration of each operation with the Performance API, which visualizes when and how long each function executes on the Chrome DevTools Performance Timeline. (Thanks to JSONPlaceholder for the dummy endpoints.)
You’re still here? Good, here comes the interesting part: writing the onclick
handler for the button
. Since all the cool kids are doing it, let’s use async
/ await
.
async function handleClick() {
someSyncOperation(); // Expensive sync operation…
I used to think that accessibility is at best a UX improvement, and at worst “compliance work”. But as the pandemic turned Boxed.com into an essential service for many of our customers, I have read a good number of heartbreaking customer service tickets that revealed my biases and the unintended exclusions caused by my code. Now, I’m convinced that making the web accessible is the right thing to do.
Accessibility is a broad topic, and a subset of loftier inclusive design principles that I won’t pretend to be an expert on. …
This is a spiritual sequel to this article.
Create a class with a method using Traditional function like so. Let’s call this Approach A.
// APPROACH Aclass SomeClass {
constructor() {
this.someProp = 'someValue';
} someMethod() { // Traditional function
console.log(this.someProp);
}
}
Create an instance of that class. When invoking the method on the instance, this
refers to the instance. So far, it’s behaving as expected.
let instance = new SomeClass();instance.someMethod(); // logs 'someValue'
But, as soon as we assign the method to a variable and call that function variable, the method loses its context, and you get Uncaught TypeError: Cannot read property ‘someProp’ of undefined.
…
Here is an explainer of how cookies work. TLDR:
Set-Cookie: cookie=monster
header, which sets the cookie in the browser.Cookie: cookie=monster
header.I store a CSRF token in a cookie, which is used by the server to validate client-side HTTP POST
requests. The server responds with a 403 if the cookie is missing in the HTTP request.
On the client-side, I have been using the cross-fetch package via the ponyfill approach.
import fetch from 'cross-fetch';fetch('/some-route', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: { /* some payload */ }…
When defining a method on an object, we presumably want this
to refer to the object on which the method is defined.
const someObj = {
someProp: 'someValue',
someMethod: function() {
console.log(this.someProp);
}
};someObj.someMethod(); // will log 'someValue'
Since all the cool kids do it, I use destructuring assignments all over the place. However, destructuring a method from an object causes it to lose its original context. In other words, when invoking a destructured method, this
no longer points to the object on which the method is defined.
const someObj = {
someProp: 'someValue',
someMethod: function() {
console.log(this.someProp); …
About