Sunday, July 5, 2020

refactoring to more idiomatic react keeping argument names with the spread operator

I'm making my first large React-based personal project, and I caught myself putting too many plain old function calls in my JSX, stuff where I might have a function like

const renderRowLabels = (soundSet, keyCurrentlyPressed) => {

being called by code in (in JSX) like

{renderRowLabels(soundSet, keyCurrentlyPressed)}

it's more idiomatic in React to make such DOM-element corresponding things look more like a tag... so the function becomes

const RowLabels = ({soundSet, keyCurrentlyPressed})  => {

since those two arguments will be passed as keys inside a props object, but we can destructure them on the fly so we don't have to refactor any code below.

Then we change the call to something like
<RowLabels soundSet={soundSet} keyCurrentlyPressed={keyCurrentlyPressed} />

But that's a little ugly, right? Redundant. With ECMAScript hotness, we can take advantage of how we are using the same names for variables in both places, and packup a nice properties bit again with the ... operator:

<RowLabels {...{ soundSet, keyCurrentlyPressed }} />

A little weird looking the first time you see it, but more true to how React sort of wants to be written...

(Note this assumes you are using the same variables names in the parent and the child. Otherwise feel free to mashup this use of the spread operator with regular properties, like
<RowLabels {...{ soundSet}} key={keyCurrentlyPressed} />)

I remember a few months ago when I first "got" the spread operator - how it feels like letting things bust out from serial to parallel, somehow...

Sunday, June 28, 2020

the joy of craft

My friend linked to Craft is Culture which had a big emphasis on Working From Home as standard practice. (Oddly I think that's what is using the term "Digital by Default" for, which is a rather misleading way of putting it, IMHO.)

Danco writes
There are many reasons why the west coast won, but one of the most widely agreed-upon was the fact that California state law forbids non-compete clauses.
I only agree with the part of that before the comma. For my career since the mid 90s at least, they've always been thought of as probably not-enforceable and generally ignored, though obviously the same explicit-IP-protections apply.

I'm not sure of all the reasons for the coast migration. MIT + Harvard and then military contracts like Lincoln Labs and Raytheon were early anchors. But other companies like Intel, Microsoft, Atari, Apple, those were all West Coast, and the new anchors.

I think that there's a limited time when a techie is likely to make a big move to follow a job (though I guess I can think of more examples than I first realized if I try). But there's a big anchoring effect... you go to college, then you either stay in that area, return to your hometown, or maybe land a new job someplace new to you. Then there's about one period in your late 20s where you might relocate again.

The thing about follow your craft is... I think the majority of techies are rather bad at monetizing their craft. They really rely on businesses to make money. Many coders are as stupid about the very basics of how a business can scale up to afford salaries (and health care) and rent - as stupid about those basics as the non-techies are about a full website works. So the 40 hour grinds continue.

It follow from that that I think most techies are fairly risk averse. They might takes some swings w/ a low salary but uncertain high reward in a startup, but that's about it. Entrepreneurial crafters are not so common.

Danco brings up the Cathedral and the Bazaar - and indeed, Linux is amazingly impressive. But I think projects like that work by programmers "scratching itches" as they say. I think Linux was an usually fortuitous mix of some folks who wanted to take on the "big itch" of a whole damn OS, and a legion of people happy to work a bit smaller. Going back to my earlier point, folks coding for the love of craft are usually not working on something that will be as universally useful as Linux has been - or anything that they have a real hope of making a living off of.

easy set of radio buttons in react

It's funny, a few basic elements are a bit tricky in pure-function React - but then again checkboxes  and radio buttons and select/options could be a bit of a semantic pain in JQuery too.

(One real pain was using setInterval - I can use the approach outlined here but it's kind of brittle, like it needs to be inside the render...)

Anyway, here is some quick and dirty code for making a set of radio buttons:
const RadioSet = ({ selectedVal, setter, valToCaptions }) => {
    return Object.keys(valToCaptions).map((val) => {
        const caption = valToCaptions[val];
        return (
            <label key={val}>
                <input onChange={() => setter(val)} type="radio" checked={val == selectedVal} />
                {caption}
            </label>
        );
    });
};
In my case selectedVal and setter are what I get from useState(), and valToCaptions is a just a simple object of key/val pairs. Nothing fancy but I stopped violating DRY with so many radio buttons...

Friday, June 26, 2020

simple checkbox in react using hooks / useState

One of those, oh right duh --
const [thereminMode, setThereminMode] = React.useState(true);
and then the checkbox:
<input checked={thereminMode} type="checkbox" onChange={() => setThereminMode(!thereminMode)} />
It took me a second to realize, we don't have to read anything from the event or the checkbox itself, the verb "change" is enough to do the toggle ourself...

preventing nativeEvent offsetX / offsetY from being blocked by child

"Last night Wes Bos saved my life"...

Well, it was this morning, not last night. And maybe "my life" is overstating it...

But Wes Bos (his React for Beginners class is stellar, and I need to get back to his Javascript 30 - seems like serious playful fun) had a tweet that was hugely useful.

For yesterday's bad react paint program turning into a music tracker, I added a large row hilight div, but it became obvious as the hilight kept jumping that it was blocking the handleMouseMove logic that was getting
const { offsetX: x, offsetY: y, buttons } = e.nativeEvent;

(The offsets were wrong, I guess because the "target" was changed)

That tweet thread suggested some weird updated math when the event target wasn't the current thing (but how to get the "current thing" in React is a little weird?) but luckily there's a trivial (and oddly CSS-based!) solution: on the child element, putting

pointer-events: none;

lets the parent div do all the math.

(That explains why some of the pixels in my bad paint program show up in the top left corner...)

Thursday, June 25, 2020

loading extra files (libraries and binary/sound files) in a parcel app

This is Parcel 101 (or maybe 102) but as I make version 2 of a vanilla js app in React using Parcel, sometimes I forget how things work in this kind of bundled app... you can't just refer to external js libraries in a script tag, and you can't just load a sound file via URL.

The library in question is pizzicato.js, and I put a bunch of files in "src/wav/sound_##_##.wav" where the ##s are two digit numbers.

The lines of code then are:

import Pizzicato from './js/Pizzicato.min.js';
import bundledFiles from './wav/*.wav';

Using Pizzicato is then straight forward (new Pizzicato.Sound() or whatever).

The sound files are a little weirder, you get a js object where the keys are what was put in the wildcard, so sound_06_27 was the key used for the ./sound_06_27.c7ca9f08.wav which was a path I could pass to Pizzicato.

Mischnic's answer on this page was helpful in figuring out the syntax.

Oh, and luckily I remembered Parcel's weirdness in assuming it is serving from the root URL of a site, I overcame that with this line in package.json:
 "scripts": {
        "build-prod": "parcel build src/index.html --public-url ."
The "--public-url" makes it happy to go where it is put.

building a bad paint program in react with parcel

I've had some success making the Atari Sound Forger, a webapp that lets you play with sound files representing noises an Atari 2600 can make by treating the computer keyboard like a specialized piano keyboard. It can even record the loops as you bang them out and play them back in browser or generate a small batari Basic program to listen on a real Atari or emulator!

One user ("Random Terrain", gatekeeper of the batari Basic command list suggested it would be more fun/useful if you could paint on the "piano roll" display ala Mario Paint or what not) - and damn it he's right.

The Sound Forger uses some semi-clever techniques with setInterval and setTimeout to let the browser do a fair job of pretending to be an Atari, but I kind of painted myself in a corner... fundamentally it's recording sound start and stop times in milliseconds (i.e. rather native to Javascript) and then extrapolating to the "frames" (60 per second) the Atari uses. (The sound engine is a pair of single voice/single timers that switches between notes and periods of silence) The algorithm screws up when you overlap sounds though... also while it worked pretty well for "sound on, sound off" approach, more complicated ADSR/sound envelope technique (i.e. hitting the note then reducing volume, like a piano striking a note) was going to be clunky at best.

So I decided to start from scratch, but grabbing tricks and techniques from my old code. Also I thought it would be good practice to switch into React, rather than doing yet another Vanilla JS project.

(Actually, it might make more sense to switch to P5.js, like I did for my playfield graphic editor playfieldpal-bB-DPC  - or maybe at least use a canvas-in-react approach which I would have to study up on, vs the "lets just throw DOM elements at it" approach I'm starting with)

I'm also a little burnt out on the "ALL THE THINGS!" approach of create-react-app, so I decided to fire up parcel. During that process, I (think I) had to update node and even download xcode on my new machine because of some js dependencies.) Also, the one bell-and-or-whistle I wanted was using CSS modules, so I had to follow a few more steps, running
npm add postcss-modules autoprefixer
and then making a .postcssrc file with
{
  "modules": true,
  "plugins": {
    "autoprefixer": {
      "grid": true
    }
  }
}
after that importing a "Widget.module.css" worked as expected and I had nice bundling of components with the CSS that applies to them only.

I decided to start with a simple Proof of Concept paint program... cutting to the chase I ended up the stuff I put in this codesandbox.io. (Out of laziness I punted and went back to normal CSS inclusion for the sandbox)

I used the useState hook, and to keep it simple I collapsed what might have been a 2D array into a map-like object by having the key be x + (y * width) (old programmer trick).

I'm plucking things out of the nativeEvent property of the event object my handleMouseMove function gets:
const { offsetX: x, offsetY: y, buttons } = e.nativeEvent;
I'm not quite sure what e.nativeEvent is... I could dump it out to console.log() ok, and pull out the stuff I need (offset values so the mouse position relative to the top left corner)  but if you go over the keys or call JSON.stringify on it it claims the only key is "isTrusted" (set to true).

So, it's a pretty lousy paint program, missing a lot of dots! (when I made a simpler editor, I used a trick of treating the previous mouse position and current as line endpoints to get all the dots inbetween). But for the app I have in mind, the grid is much less "fine" (it may even use clicks instead of drawing for the most part) and I think this technique (of putting small pixel-y <div>s  over the parent space for the dots) should work ok.