Chris Padilla/Blog


My passion project! Posts spanning music, art, software, books, and more. Equal parts journal, sketchbook, mixtape, dev diary, and commonplace book.


    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;
      };

    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

    Listen on Youtube

    Just messing around today!


    Shapely Dogs

    Playing with shapes and dog faces! 🔺🐶📦

    Who's a square boy?


    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:

    Watch on Youtube

    The steps are:

    1. Research and gather reference
    2. Study in context (breaking down the construction of the object)
    3. Practice (playing with the form, mixing and matching elements)

    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:

    1. Imitate
    2. Assimilate
    3. Innovate

    And hey, here it is for developing in a new programming language:

    1. Read documentation/forum posts/books
    2. Build a few sample apps
    3. Stray off and build a more complex, custom app

    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.


    Owls

    Yes, you

    Who, me?


    Speer - Baroque Dance

    Listen on Youtube

    So stately! 🎩


    Testing with Pytest

    A quick start guide:

    1. Install Pytest through pip.
    2. When running your tests, call it through the command line:
    python3.7 -m pytest .

    There's documentation for calling it globally, but I had no luck. Calling it through the same version of python with the -m flag did the trick for me.

    1. Write your test file!
    import pytest
    
    @pytest.fixture(scope='class')
    def my_tests_init(request):
        # Set up DB connection here
    
        try:
            # Insert any sample date
        except Exception as e:
            # Handle exceptions
    
        yield # Your tests will happen here
    
        # Tear down: remove sample data from db.
        # Close DB

    Pytest will use decorators to add in the functionality needed to interpret your file correctly.

    All your tests will live in a class like this one:

    @pytest.mark.usefixtures("my_tests_init")
    class TestMyClass:
    
        def test_my_method(self):
            expected = [{'cool': True}]
    
            result = self.my_class.my_method()
            assert result == expected

    Here, the decorator ensures the the init method runs before and after these tests.

    From there, test methods must start with "test" in the method name.

    And you do your thing from there!

    Pytest, unlike testing libraries like unittest or Jest in JS, doesn't use assertion methods. Here we just have the assert keyword being used against a boolean expression. Even if we use a reference type, Pytest will know to assert the values of the dictionary or list deeply.

    Additionally: If you're not finding what you need from the command line error logs, adding the v flag will give you a closer look, while vv gives you even more details. vv Goes as far to show a git-like comparison, line by line, of what conflicts between your expected and actual results!


    Sketchbook No. 5 Down!

    📝

    Another one down!!

    Started off very exercise heavy. These are figure study simplifications of the torso here:

    🫘

    Explored a few animal charicatures:

    🦉

    🐘

    And then finished the book out with meditative automatic drawing:

    🧘

    My next one is a bigger sketchbook, so I'm excited to have more room to play. :)


    Automatic Drawing

    Something I'm still learning is to keep more play in all of my creative practices.

    Maybe that's surprising to anyone looking in from what I share! But it's true — it's still a challenge to turn off the critical brain when I'm playing an instrument or drawing.

    With drawing, I was just doodling and messing with shape at the start. Lately, I've caught myself leaning more towards filling all of my drawing time with exercises and course. If I'm aiming for the 50% rule, I'm really only hitting 25% play. Not entirely a bad thing, but it leaves little room for experimentation and just enjoying the act of drawing.

    An experiment I'm trying out to really loosen up is automatic drawing, as demonstrated by Tim Gula here:

    Watch on Youtube

    The end result is beautiful, but the result isn't really the point. Its meditation for the mind, playing purely with shape, line, and values.

    The irony is that, in messing around, a lot of growth happens at the same time: Line quality improves, control over shape develops, and a relaxed brain allows for those periods of focus to be more energized.

    Most exciting for me, though, is that when I'm just doodling outside of any coursework, I can shrug off the pressure of it having to be impressive. There's room for experimentation and simply enjoying putting pencil to paper!

    I'm trying this out with music too. Lately, I've been spending all my piano and guitar time playing out of books, leaving no time to write or improvise. So now I have a notebook for jotting down improvised chords, melodies, and mini-tunes. Not with the goal of making another album. Just for the space to enjoy sound unto itself.

    At the end of the day, sound and line are purpose enough.


    Winston Churchill on Painting as a Pastime

    I don't always find the inspiration I'm looking for from the bios and stories of full time artists. Those select few who can spend most of their week in the studio. Winston Churchill, who was surprisingly an enthusiastic, self proclaimed hobby painter, can speak much more clearly to the situation of having to find time to be creative in the nooks and crannies of the day.

    And a surprisingly sensitive painter, at that!!

    I've been rattling in my mind all the different reasons I like drawing/piano/guitar/writing music/blogging, and why someone else might like to do them too.

    To Churchill, it was no contest comparing painting to more typical leisure activities:

    Even at the advanced age of Forty! It would be a sad pity to shuffle or scramble along through one's playtime with golf and bridge, pottering, loitering, shifting from one heel to the other, wondering what on earth to do – as perhaps is the fate of some unhappy beings – when all the while, if you only knew, there is close at hand a wonderful new world of thought and craft, a sunlit garden gleaming with light and colour of which you have the key in your waistcoat pocket. Inexpensive independence, a mobile and perennial pleasure apparatus, new mental food and exercise, the old harmonies and symmetries in an entirely different language, an added interest to every common scene, an occupation for every idle hour, an unceasing voyage of entrancing discovery – these are high prizes.

    A particularly sobering passage acknowledges that, as a hobbyist, you won't reach the same great heights of technique as the life-long, full time painters. It's a problem I find myself wrestling with frequently, but Churchill brings it up so matter-of-factly and without much concern:

    But if, on the contrary, you are inclined – late in life though it be – to reconnoitre a foreign sphere of limitless extent, then be persuaded that the first quality that is needed is Audacity. There really is no time for the deliberate approach. Two years of drawing-lessons, three years of copying woodcuts, five years of plaster casts – these are for the young. They have enough time to bear. And this thorough grounding is for those who, hearing the call in the morning of their days, are able to make painting their paramount lifelong vocation. The truth and beauty of line and form which by the slightest touch or twist of the brush a real artist imparts to every feature of his design must be founded on long, hard, persevering apprenticeship and a practice so habitual that it has become instinctive. We must not be too ambitious. We cannot aspire to masterpieces. We may content ourselves with a joy ride in a paintbox. And for this Audacity is the only ticket.

    These trees ❤️

    It's still worth the pursuit all the same. The rewards for that Audacity are many:

    I have written in this way to show how varied are the delights which may be gained by those who enter hopefully and thoughtfully upon the pathway of painting; how enriched they will be in their daily vision, how fortified in their independence, how happy in their leisure. Whether you feel that your soul is pleased by the conceptions or contemplation of harmonies, or that your mind is stimulated by the aspect of magnificent problems, or whether you are content to find fun in trying to observe and depict the jolly things you see, the vistas of possibility are limited only by the shortness of life. Every day you may make progress. Every step may be fruitful. Yet there will stretch out before you an ever-lengthening, ever-ascending, ever-improving path. You know you will never get to the end of the journey. But this, so far from discouraging, only adds to the joy and glory of the climb.

    So beautifully said. It all rings true for me:

    • Playing with shapes and sound, their interaction with one another, is fun unto itself
    • Creation is largely puzzle solving. Even playing piano is a finger puzzle
    • There's no better way to express love for the world than to try to recapture it as you see it
    • Progress, itself, is a joy! Learning an instrument more intimately or refining a line stroke, they all lead to small victories
    • There are infinite paths to explore. None right or wrong, only guided by your curiosity and interest

    And why wait? The time to plant a garden is now:

    Try it, then, before it is too late and before you mock at me. Try it while there is time to overcome the preliminary difficulties. Learn enough of the language in your prime to open this new literature to your age. Plant a garden in which you can sit when digging days are done. It may be only a small garden, but you will see it grow. Year by year it will bloom and ripen. Year by year it will be better cultivated. The weeds will be cast out. The fruit-trees will be pruned and trained. The flowers will bloom in more beautiful combinations. There will be sunshine there even in the winter-time, and cool shade, and the play of shadow on the pathway in the shining days of June.

    While the earlier, the better for overcoming the initial hurdle of learning the vocabulary, it's never too late. Thankfully, the barrier to entry is not technique or years of experience. Just Audacity!

    ☁️