Chris Padilla/Blog

My passion project! Posts spanning music, art, software, books, and more
You can follow by Newsletter or RSS! (What's RSS?) Full archive here.

    Utility Classes in Bootstrap

    TIL that Bootstrap has a few utility classes for customizing beyond the basic styles.

    This was a surprise to me! My experiences with Bootstrap has been to quickly put together prototypes and learning projects. So custom templating was not a high priority. Whatever came out of the box worked fine.

    Though, in my current case, I had a situation where I wanted to adjust spacing. In Tailwind or a CSS-in-JS environment, granular updates are easy enough. But in my current project, I've only been using bootstrap styles. So I was looking at a potential break in flow if I reached for another css library or fired up a css file just for a few custom spacing classes.

    Not the end of the world to go with those options, but it's handy to see that Bootstrap had me covered!

    If you're familiar with Tailwind, these will look pretty familiar. Here's the example from Bootstrap's docs:

    .mt-0 {
      margin-top: 0 !important;
    }
    
    .ms-1 {
      margin-left: ($spacer * .25) !important;
    }
    
    .px-2 {
      padding-left: ($spacer * .5) !important;
      padding-right: ($spacer * .5) !important;
    }
    
    .p-3 {
      padding: $spacer !important;
    }

    Actually, looking through the Utilities sections of the docs, there are several utility classes that I commonly reach for in tailwind:

    • Background
    • Borders
    • Colors
    • Display
    • Opacity
    • Position
    • Sizing
    • Vertical Align
    • Z-Index

    It doesn't cover the whole spectrum of CSS in the same way that Tailwind does, of course. Off the top of my head, I know that CSS transition properties are missing. Really, though, Tailwind is solving a different problem ā€” atomic classes to reduce premature abstraction while still keeping a highly flexible design system, whereas Bootstrap is a CSS framework.

    I've largely stayed away from CSS frameworks in my personal projects. I'm comfortable with writing custom CSS, and I like the flexibility of writing by hand. I've been delightfully surprised with using Bootstrap lately, though. You could supplement any CSS library with utility classes through Tailwind or combine with custom CSS.

    But, in cases where I want an all in one solution, I'll be keeping an eye out for libraries that expose their own utility classes.

    My Comfort Work and "Do You Like Sentences?"

    Technique is a bit of a vice for me.

    When in doubt, I draw boxes.

    When in doubt, I play scales.

    When in doubt, I mess around with a toy app in software.

    It's my vice because I'm guilty of probably practicing too much technique and not just making things more often. But there it is. It's my "comfort work" as Austin Kleon puts it.

    To each their own. It gets me in the studio, in front of the piano, and in my code editor. Usually, that's where I need to start to get to the really good stuff, anyway.

    I'm at least in good company. In The Writing Life, Annie Dillard shares an interaction between a fellow authorand a student where the student asks "Do you think I could be a writer?" Their response:

    "Do you like sentences?"

    I like sound and lines. I even like the colored text in VS Code! So I'll take it as a sign that I'm in all the right crafts.

    Liszt ā€“ Liebestraum No 3 (arranged)

    Listen on Youtube

    šŸ’­šŸ’™

    The original is stunning. Someday!

    A Good Pup

    Saw a dog doing a good sit just like this on a bike ride

    šŸ¶

    Data Fetching with React / TanStack Query

    TanStack Query (formerly React Query) is a delightful library for handling state around fetching data. If you're familiar with Apollo Client for GraphQL, but aren't using GraphQL in your app, TanStack Query gives you many of the same benefits:

    • Data Caching
    • Refetching at an interval
    • Data Fetching State (such as isLoading or error)

    If you're coming from a redux environment, much of the above was managed by hand. TanStack Query, however, takes care of the above automatically, while still exposing opportunities to make manual fetches when needed.

    Set Up

    The quick start in the official docs should be enough to get you going.

    The gist is you'll wrap your app in a QueryClient provider. Similar to the Redux provider, this will store your data at an application level.

    import React, { Component } from 'react';
    import { Route, Routes } from 'react-router-dom';
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
    import AppRoutes from './AppRoutes';
    import { Layout } from './components/Layout';
    import './custom.css';
    
    const App = () => {
      const queryClient = new QueryClient();
    
      return (
        <QueryClientProvider client={queryClient}>
          <Layout>
            <Routes>
              {AppRoutes.map((route, index) => {
                const { element, ...rest } = route;
                return <Route key={index} {...rest} element={element} />;
              })}
            </Routes>
          </Layout>
        </QueryClientProvider>
      );
    };
    
    export default App;

    Sending a Query

    TanStack Query is not a replacement for fetch. Its focus is the state and caching of your data. So you'll still need to write a query function:

      const fetchBooks = async () => {
        const response = await fetch('book');
        const data = await response.json();
        return data;
      };

    Once you have it, you'll pass it into the useQuery hook with a couple of options:

      const { data, isLoading } = useQuery({
        queryKey: ['books'],
        queryFn: fetchBooks,
      });

    The queryKey is an array of values that will help keep your query labeled. This is helpful in the event you want to invalidate the query, essentially asking for another network request to be sent to update the data in the cache.

    From the result, we're destructuring what we need: the data and the state isLoading.

    isLoading can be used to show a loader to the user in your UI:

      return (
        <div>
          <h1 id="tableLabel">Books</h1>
          <p>This page demonstrates fetching data from the server.</p>
          <div>
            {isLoading ? (
              <p>
                <em>Loading...</em>
              </p>
            ) : (
              renderTable(data)
            )}
          </div>
        </div>
      );

    isLoading pertains to the initial fetch. For the state of another query being sent, you'll want to reach for isFetching.

    Query With Filters

    Sometimes you just need one book. Query keys are commonly used for passing arguments to your fetch methods.

    Here's the useQuery setup:

      const { data, isLoading } = useQuery({
        queryKey: ['books', { id }],
        queryFn: fetchBook,
      });

    And here's how we access the arguments in the query function:

      const fetchBook = async ({ queryKey }) => {
        const [, { id }] = queryKey;
        const response = await fetch(`book/edit/${id}`);
        const data = await response.json();
        return data;
      };