Handling hard-coded values

Originally published Oct 28, 2021·Tagged #software-architecture, #web-development, #javascript

Hard-coded values -- typically content -- are often set up for rapid development in the early stages of a project. These values can get outdated pretty quickly, and can be hard to replace with dynamic values. Here are some suggestions for dealing with hard-coded values.

Use default props for hard-coded content

It's tempting to simply inline hard-coded content directly into the components you're writing. For example:

import * as React from 'react';
import * as styles from './ProductCard.module.css';

export default function ProductCard() {
  return (
    <div className={styles.card}>
      <img className={styles.cardImg}
        src="https://picsum.photos/200"
      />
      <h3>Running Shoes - $39.99</h3>
      <p><small>New as of October 28, 2021!</small></p>
      <p>Our best-selling running shoes.</p>
    </div>
  );
}

This will quickly get something onto the screen. However, it raises a few questions:

  • Which elements will remain the same for every product, and which elements will change?
  • Which elements might be shown or hidden for different products?
  • What format should the data be passed in?

Instead of inlining the content, try using defaultProps to provide it:

import * as React from 'react';
import * as styles from './ProductCard.module.css';

export default function ProductCard({
  productImgSrc,
  title,
  priceCents,
  promoMessage,
  description,
}) {
  const displayPrice = `$${(priceCents/100).toFixed(2)}`;
  return (
    <div className={styles.card}>
      <img className={styles.cardImg}
        src={productImgSrc}
      />
      <h3>{title} - {displayPrice}</h3>
      {
        !promoMessage ? null : (
        <p><small>{promoMessage}</small></p>
        )
      }
      <p>{description}</p>
    </div>
  );
}

ProductCard.defaultProps = {
  productImgSrc: 'https://picsum.photos/200',
  title: 'Running Shoes',
  priceCents: 3999,
  description: 'Our best-selling running shoes.',
  promoMessage: 'New as of October 28, 2021!',
}

Now, it's much easier to see what is dynamic, as well as which properties are optional. It's important that these default props be removed once you've moved past the demo stage, as you certainly don't want to push dummy values to production by accident, but this is true for any hard-coded demo content.

Move hard-coded content to its own file

Since you've already got your props broken out into defaultProps, it's pretty easy to move them to their own file used for content only. I like to move these files into an adjacent __tests__/data directory. This way:

  1. it's super clear that this is fake data not meant for production, and
  2. the data is readily available for writing tests. Two birds, one stone!

Simulate endpoint responses for your fake data

This one is highly optional, but is a very helpful final step before updating your code to pull its content from an endpoint instead of local fake data.

Up until now, our fake data has been directly imported. When we fetch from a real endpoint, though, we won't have the luxury of requesting these values synchronously — we'll have to use Promises.

Luckily, most modern bundlers handle code splitting, and we've already split our content into a separate file. This means that we've already got a Promise-based way to request our data!

// ... inserted into existing file ...
import useSWR from 'swr';

/** It's helpful to add future parameters here. */
function useProductData(productId) {
  return useSWR(
    ['productData', productId],
    () => import('./__tests__/data/product-data.tsx')
  );
}

At this point, the only task remaining is to swap out the import statement for an actual fetch-based implementation.

What surprises have you discovered when working with hard-coded values?