Chris Padilla/Blog
My passion project! Posts spanning music, art, software, books, and more. Equal parts journal, sketchbook, mixtape, dev diary, and commonplace book.
- Background
- Borders
- Colors
- Display
- Opacity
- Position
- Sizing
- Vertical Align
- Z-Index
- Data Caching
- Refetching at an interval
- Data Fetching State (such as
isLoading
orerror
) - Research and gather reference
- Study in context (breaking down the construction of the object)
- Practice (playing with the form, mixing and matching elements)
- Imitate
- Assimilate
- Innovate
- Read documentation/forum posts/books
- Build a few sample apps
- Stray off and build a more complex, custom app
Spanish Melody
Picked up a new book for this pretty instrument.
Moose!
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:
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)
A Good Pup
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:
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;
};
Service and Expression
One of the best distinctions I made a few years ago was understanding that my work would be balanced by two spheres: Service and expression.
I dipped my toes into SVSLearn's illustration podcast on an episode about what it's like diving into the comics industry. Not my personal goal, but I've been reading a lot of comics and I thought it would be interesting!
Right out the gate, the guest described his work as a rollercoaster ride between the stable, steady work in animation and writing comics, where there's comparatively no money to be made.
Tim Ferris describes a similar back and forth in the Four Hour Work Week, coining the term"mini retirements." Tim would work his butt off launching a product or project, working 12-16 hour days, and then spending those same 12-16 hours in brazil learning salsa dancing for three months.
A little extreme for my personal taste and for this phase of life. To each their own, but in my experience it's a lot more sustainable to work a bit of both into your daily life.
Back to the podcast: the guest describes a tension creative people fill: to have the stability and camaraderie of working on a team to bring someone else's vision to life β and the tension to create something wholly your own, and have your own voice fully expressed.
I think we all experience a bit of that, and we all find our own ways to fill those needs. Understanding that there's a difference between the purpose of those two spheres of work makes a world of a difference, though.
My software work isn't dragged down by ego. Because I have a few options for expression and play, I can more fully do work during the day that best serves the interest of my colleagues.
And vic versa. When I was a music teacher, my creative work felt like it had to be tied with teaching. I didn't feel like I could really explore visual art or piano or writing music because my full time gig was playing and teaching saxophone. Surely I had to be putting my free time in on the horn, too! Now, because my daytime work is a world apart, there's no baggage around what I should be doing to express myself. Heck, I've said it before, I'll say it again β blogging has surprisingly been one of the best ways to fulfill that itch!
Calling those halves service and expression works for me, but maybe Tony Robin's terms of Stability and Instability works better for you. Or maybe Brene Brown's guidepost to find "Meaningful Work." I'm a person that's driven primarily by the desire to create. But say that you are actually primarily service driven. Then, the two spheres might include stable service and unstable service. That stable service of holding a marketing job that pays the bills, balanced with the unstable (but more fulfilling) service work of non-profit contributions and volunteering.
What ever the words really are β finding balance between two complimentary needs can make the world of a difference in everyday fulfillment.
Faber - Tropical Island
COLOR!! π
My sister very kindly loaned me her Copics to play with!
After months in greyscale, it's fun to have shades and hues to work with!
Perfect opportunity to do this Satsuki study from My Neighbor Totoro! Borrowed from this beautiful Miyazaki art book.
From MVC to API Routes in ASP.NET Core
MVC patterns make for quick and easy development in .NET Core. This week, I'm switching over to an API servicing a React application, and there are a couple of small differences in getting started with serving up API endpoints:
ApiController Attribute
In MVC, all we needed to setup a controller was inherit from the controller class:
namespace BulkyBookWebDotNet6MVC.Controllers
{
public class CategoryController : Controller
{
. . .
}
}
For API's, we need to go up the family tree to ControllerBase
, which controller inherits from:
namespace BulkyBookWebDotNet6MVC.Controllers
{
public class CategoryController : ControllerBase
{
. . .
}
}
Attributes
In addition, we need a few different attributes:
[ApiController]
[Route("[controller]")]
public class BookController : ControllerBase
{
. . .
}
[ApiController]
will give goodies specific to APIs, such as specifying routes and more detailed error responses.
Since we have to specify routes, we're doing so with the next attribute [Route("[controller]")]
We're defaulting to the controller based routing, but we could just as well define our own route such as [Route("/users")]
Return
Of course, since we're working with an API returning data and not a View from an MVC, the return methods will be slightly different. [ApiController]
provides methods that are analogous to Status codes:
if (BookFromDb == null)
{
// Status: 404
return NotFound();
}
// Status: 200 with created object
return Ok(BookFromDb);
And that should be enough to get started! More details are available on MSDN.
C Fingerstyle Barre Improv
Just messing around today!
Shapely Dogs
Dependency Injection and Logging in .NET
Buy in large, server logs come out of the box with .NET Core. Setting up logging for you controllers, however, takes just a little bit of setup.
In console applications, logging is straightforward:
System.Console.WriteLine("Hello World");
In a .NET application, though, you'll likely need to pipe your logs through the logger class from your Dependency Injection.
Here's a look at a bare bones Controller:
using Microsoft.AspNetCore.Mvc;
namespace Bookshelf_cs.Controllers;
using Bookshelf_cs.Data;
using Bookshelf_cs.Models;
public class AuthorController : ControllerBase
{
private readonly ILogger<AuthorController> _logger;
public AuthorController(ILogger<AuthorController> logger)
{
_logger = logger;
}
}
In the constructor, we're constructing our controller with a _logger
attribute.
An aside on Dependency Injection
So why is this better than just instantiating a logger class directly? Like so:
private readonly ILogger<AuthorController> _logger = new Logger();
The short answer is to avoid tight coupling. By using the ILogger
interface, you can swap out the default Microsoft logger with a custom logger so long as it shares the same interface. More details with examples on MSDN.
That swapping out happens at your app building stage in your Program.cs file. If we wanted to customize the logger, it would look like this:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Logging.AddConfiguration(configuration.GetSection("Logging"));
var app = builder.Build();
Very DRY. If you need to make changes, it's all done at the application level. From there, the builder injects your logger of choice into your classes that accept a logger in their constructors.
Logging
Once it's instantiated in your class, you can log directly from the _logger
attribute:
_logger.LogInformation(1001, "Author passed");
_logger.LogWarning(1001, obj.ToString());
For any custom changes for what level of logs are displayed, you can tweak the appsettings.json file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.AspNetCore.SpaProxy": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
How To Make (Anything) In 3 Steps
I came across Ctrl + Paint recently, another great and nearly free resource for learning to draw.
They have a fantastic video on how to draw anything in a few steps:
The steps are:
The glossed over step 0 is to understand the foundations: "How to render form and light." No small step! But, once you have the vocabulary in place, that's how you handle drawing something new from imagination.
A recurring theme on ol' Chris D Padilla dot com is that everything is the same! Here, for example, is how Clark Terry talks about learning to play and be creative in jazz:
And hey, here it is for developing in a new programming language:
The trick behind it, and why the title of Ctrl + Paints video skews just a smidge click-baity, is that each step takes time. The reward, though, is that when you're comfortable with this process, you literally can make just about anything. No need to know everything before starting a project, and no need to master everything right away. Just follow the cycle, and iterate from there.