Wednesday, October 5, 2022

Zustand computed values

This week I’ve faced some situations with Vue’s reactive that alarmed me. I’m finding the hard way what I’ve read a couple times: reactive proxies can behave unpredictably in some situations. Too bad I’m in the middle of a project which began in Vue 3. I’m strongly considering rewriting it in React now – yes, it will be an insane amount of work, including back-end changes to return normalized objects.

But React brings its own problems. The biggest of all is certainly the raw state management.

Among all state management tools I’m evaluating, Zustand is showing to be the most promising. It’s ticking all the boxes, and the only open question so far is computed state. The best I could do was to use custom hooks, but they look rather ugly and verbose:

import create from 'zustand';
import {combine} from 'zustand/middleware';

/**
 * The store, which holds the state and the actions.
 */
const useBearStore = create(
	combine({
		bears: 0,
	},
	(set, get) => ({
		increasePopulation(): void {
			set(state => ({ bears: state.bears + 1 }));
		},
		removeAllBears(): void {
			set({ bears: 0 });
		},
	})),
);

export default useBearStore;

/**
 * Custom hook that returns a computed value.
 */
export function useBearCountPlusOne(): number {
	const bears = useBearStore(s => s.bears);
	return bears + 1;
}

Usage example in a component:

import useBearStore, {useBearCountPlusOne} from './useBearStore';

function App() {
	const increasePopulation = useBearStore(s => s.increasePopulation);
	const populationPlusOne = useBearCountPlusOne();

	return <>
		<div>Population + 1: {populationPlusOne}</div>
		<button onClick={() => increasePopulation()}>
			Increase population
		</button>
	</>;
}

If I find a way to rework these loose hooks, I think I found my way.

Wednesday, September 28, 2022

Multiple className values in React

React offers basically zero support for CSS. Personally I’m fond of CSS Modules along with SCSS instead of the slow CSS-in-JS solutions out there. Still, we need a way to manage multiple classes in className property.

In order to mitigate this problem, I wrote a TypeScript function to deal with sequential or conditional class names situations:

/**
 * Generates the className attribute with the given class names.
 */
export function cls(...names: (string | undefined)[]): string;
export function cls(names: (string | undefined)[]): string;
export function cls(names: Record<string, boolean>): string;
export function cls(names: any): string {
	if (typeof names === 'string' && arguments.length === 1) { // just 1 string
		return names === undefined ? '' : names;
	} else if (typeof names === 'string' && arguments.length > 1) { // multiple strings
		let s = '';
		for (const name of arguments) {
			if (name !== undefined) s += name + ' ';
		}
		return s;
	} else if (Array.isArray(names)) { // string array
		let s = '';
		for (const name of names) {
			if (name !== undefined) s += name + ' ';
		}
		return s;
	} else if (typeof names === 'object') {
		let s = '';
		for (const name in names) {
			if (names[name]) s += name + ' ';
		}
		return s
	} else {
		throw 'Invalid class name input!';
	}
}

Example using the 4 argument possibilities:

import {cls} from '@/funcs';
import c from './App.module.scss';
	
function App() {
	return <>
		<div className={cls( c.first )} />
		<div className={cls( c.first, c.second )} />
		<div className={cls( [c.first, c.second] )} />
		<div className={cls({
			[c.first]: true,
			[c.second]: false,
		})} />
	</>;
}

This covers all situations I ever faced.

The spread operator version was added in February 9, 2023.

Thursday, July 21, 2022

Sizes of Windows integral types

While developing WinSafe, it’s very common to convert the Windows integral data types to their Rust equivalent. Care must be taken, however, when it comes to pointer size, which varies according to the architecture. Since WinSafe is aimed to both 32 and 64-bit Windows, I must pay attention.

For reference, below is the table I’m using to figure out the sizes:

Signed C Signed Rust Unsigned C Unsigned Rust 32-bit 64-bit
CHAR
 
 
i8 UCHAR
BYTE
BOOLEAN
u8 8 bit (1 byte)
SHORT
 
 
i16 USHORT
WCHAR
WORD
u16 16 bit (2 byte)
BOOL
INT
LONG
 
i32  
UINT
ULONG
DWORD
u32 32 bit (4 byte)
INT_PTR
LONG_PTR
LPARAM
 
isize UINT_PTR
ULONG_PTR
WPARAM
SIZE_T
usize 32 bit (4 byte) 64 bit (8 byte)
LARGE_INTEGER
LONG64
LONGLONG
 
 
i64 ULARGE_INTEGER
ULONG64
ULONGLONG
DWORD64
DWORDLONG
QWORD
u64 64 bit (8 byte)

The table above is an extension of this one.

Tuesday, July 19, 2022

Default props in React function components

Having default props in a React component is a rather common situation. The most popular way to accomplish this is to pass the default values to a defaultProps property on the function component. However, this property will be deprecated in the future.

Spoiler: due to the sheer amount of code written with it, it never will be deprecated. It’s more likely that a warning will show in the console.

Anyway, in order to keep things clean and guard from this future warning, I came up with a clean pure TypeScript solution to this problem:

interface Props {
	name: string;
	surname?: string;
	age?: number;
}

const defaultProps = {
	surname: 'Doe',
};

function MyComponent(props0: Props) {
	const props = {...defaultProps, ...props0};

	return <div>{props.surname}</div>;
}

The code above provides the correct behavior and proper TypeScript validation. It ended becoming an answer on StackOverflow.

Since the spread order may be a a bit hard to remember, this function does the trick:

export function defProp<P, D>(props: P, defaultProps: D): P & D {
	return {...defaultProps, ...props};
}

function MyComponent(props0: Props) {
	const props = defProp(props0, defaultProps);

	// ...
}

Thursday, July 7, 2022

Extending built-in TypeScript objects in Vite

Checking whether an array is empty is a rather common operation. The usual way to do this in TypeScript is by checking the length property, but it’s very verbose:

if (myArray.length === 0) {
	// ...
}

For some reason, the JavaScript standard doesn’t specify an isEmpty() method. Fortunately, we can extend native JavaScript objects to add any methods we want. While this is a great feature, I was unable to make it work in a Vue + TypeScript + Vite project. I want the autocomplete of my VSCode to properly display it.

To make the TypeScript compiler recognize it, you must declare your method extensions as global by wrapping the declaration in a declare global block.

At first I tried to write the declaration in the “src/env.d.ts” file, but it didn’t work. Turns out you need to place them in a different file, as explained here. So I finally wrote them in a “src/extensions.d.ts” for the typings, and then the implementations themselves must be in a file that will be actually called in your application:

I tested the code above in a Vue project, but I believe it will work accordingly in a React project as well.

Wednesday, June 22, 2022

Checking if user passed a slot in Vue

In Vue 3 with the Composition API, there is no this.$slot entry to programmatically poke on the slots. You must summon the slots by calling useSlots() – yes, that’s a React hook right there.

The returned object has one entry for each slot. If your component has only one unnamed slot, it will be named 'default'. So, in order to check whether the user didn’t pass the slot, you simply check whether the entry exists:

<script setup lang="ts">
import {computed, useSlots} from 'vue';

const slots = useSlots();
const hasSlot = computed(() => slots['default'] !== undefined);
</script>

Tuesday, June 21, 2022

Global useState hooks with Jotai

I’ve been stressing out several React global state libraries in the past months. Last week it was Jotai’s time.

I liked the concept of “atoms” and how they feel like autonomous useState parts, and how they can be share state among components. I remember trying to write something like this in the past. Jotai seems to be what I tried to do back then:

import {atom} from 'jotai';

export const nameAtom = atom('hello');

Pretty much a global useState here, which is great:

import {useAtom} from 'jotai';
import {nameAtom} from './state';
	
function App() {
	const [name, setName] = useAtom(nameAtom);

	//...
}

In large, real-world applications you’d like to write mutation methods to implement specific logic, rather than having them scattered over the components. Thus we should not make setName public; instead we should provide more specific methods.

In Jotai, while reading the state is trivial, I found writing mutations to be rather cumbersome. The syntax of “writing atoms” is, to my tired eyes, very convoluted:

export const setSurnameAtom = atom(null, (get, set, surname) => {
	set(nameAtom, get(nameAtom) + ' ' + surname);
});

//...

function App() {
	const [, setSurname] = useAtom(setSurnameAtom);
}

After giving it some thought, it occurred me that since useAtom is a hook, I can compose a custom hook over it. And then it all clicked:

export function useName() {
	const [name, setName] = useAtom(nameAtom);
	return useMemo(() => ({
		value: name,
		setSurname(surname: string) {
			setName(name + ' ' + surname);
		},
	}), [name, setName]);
}

Usage of this custom hook is straightforward, crystal clear:

import {useName} from './state';
	
function App() {
	const name = useName();

	return <>
		<h1>{name.value}</h1>
		<button onClick={() => name.setSurname('foo')}>
			Set surname
		</button>
	</>;
}

This is a truly global custom useState hook. This works amazingly well with VSCode autocomplete. This is easy to read. This is beautiful.

In the custom hook above, note the use of useMemo. It cuts down a lot of the processing in inside the custom hook, and it was an insight I had after briefly talking to Daishi Kato himself, the author of Jotai, about this custom hook idea. The future useEvent hook will optimize the button call a little further.

Jotai, unfortunately, has a huge drawback of not allowing accessing atoms outside a React component.

Wednesday, June 1, 2022

React high-order useEffect wrappers

As I was increasingly upset with the lack of variable highlighting in Volar, I started doing some React experiments.

In particular, I found the useEffects hook API very annoying, because it fails to communicate the intent of what you’re trying to accomplish.

So, I order to mitigate this, I wrote a few wrappers:

import {DependencyList, useEffect} from 'react';

function onMount(fun: () => void) {
	useEffect(() => { fun(); }, []);
}

function onUmnount(fun: () => void) {
	useEffect(() => {
		return () => { fun(); };
	}, []);
}

function onMutate(dep: DependencyList, fun: () => void) {
	useEffect(() => { fun(); }, dep);
}

As far as I could test, they work really well, with the advantage that you can pass async functions to them:

function Foo() {
	const [name, setName] = useState('');

	onMount(async () => {
		console.log('hello async');
	});

	onUmnount(() => {
		console.log('unmounted');
	});

	onMutate([name], () => {
		console.log('updated', name);
	});

	return <div>Hello {name}</div>;
}

I’m somewhat tempted to create a library for this. And maybe other high-order wrappers.

Monday, May 23, 2022

Cross-compiling Rust in Windows

After making a lot of confusion with Rust cross-compiling, I finally managed to compile WinSafe x32 programs in my Windows x64. The root of the misunderstanding is that, in order to cross compile, you must have the following installed:

  • MSVC build tools;
  • Rust toolchain;
  • Rust target.

Toolchain relevant commands:

rustup toolchain list
rustup toolchain install stable-i686-pc-windows-msvc
rustup toolchain uninstall stable-i686-pc-windows-msvc

Target relevant commands:

rustup target list | grep installed
rustup target add i686-pc-windows-msvc --toolchain stable
rustup target remove i686-pc-windows-msvc

To verify if you program has any linker issues, build and run:

rustup run stable-i686-pc-windows-msvc cargo run
rustup run stable-x86_64-pc-windows-msvc cargo run

Then finally the program can be built for release with:

RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target i686-pc-windows-msvc
RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-pc-windows-msvc

I only found all that stuff after posting a question on StackOverflow and receiving this comment. The documentation was completely absent in providing any useful information.

Monday, May 9, 2022

Taking a screenshot in Windows with Go

I’ve tried to code a Windows screenshot utility before, following the example of the official Windows documentation I found it tricky though, so I just gave up at the time.

This Monday morning, after receiving a request to implement GetDIBits in WinSafe, I tried to implement it Windigo first. To my surprise, it went incredibly smooth. Go’s defer mechanism is much to praise.

Here’s the whole code

package main

import (
	"runtime"
	"unsafe"

	"github.com/rodrigocfd/windigo/win"
	"github.com/rodrigocfd/windigo/win/co"
)

func main() {
	runtime.LockOSThread()

	cxScreen := win.GetSystemMetrics(co.SM_CXSCREEN)
	cyScreen := win.GetSystemMetrics(co.SM_CYSCREEN)

	hdcScreen := win.HWND(0).GetDC()
	defer win.HWND(0).ReleaseDC(hdcScreen)

	hBmp := hdcScreen.CreateCompatibleBitmap(cxScreen, cyScreen)
	defer hBmp.DeleteObject()

	hdcMem := hdcScreen.CreateCompatibleDC()
	defer hdcMem.DeleteDC()

	hBmpOld := hdcMem.SelectObjectBitmap(hBmp)
	defer hdcMem.SelectObjectBitmap(hBmpOld)

	hdcMem.BitBlt(
		win.POINT{X: 0, Y: 0},
		win.SIZE{Cx: cxScreen, Cy: cyScreen},
		hdcScreen,
		win.POINT{X: 0, Y: 0},
		co.ROP_SRCCOPY,
	)

	bi := win.BITMAPINFO{
		BmiHeader: win.BITMAPINFOHEADER{
			BiWidth:       cxScreen,
			BiHeight:      cyScreen,
			BiPlanes:      1,
			BiBitCount:    32,
			BiCompression: co.BI_RGB,
		},
	}
	bi.BmiHeader.SetBiSize()

	bmpObj := win.BITMAP{}
	hBmp.GetObject(&bmpObj)
	bmpSize := bmpObj.CalcBitmapSize(bi.BmiHeader.BiBitCount)

	rawMem := win.GlobalAlloc(co.GMEM_FIXED|co.GMEM_ZEROINIT, bmpSize)
	defer rawMem.GlobalFree()

	bmpSlice := rawMem.GlobalLock(bmpSize)
	defer rawMem.GlobalUnlock()

	hdcScreen.GetDIBits(hBmp, 0, int(cyScreen), bmpSlice, &bi, co.DIB_RGB_COLORS)

	bfh := win.BITMAPFILEHEADER{}
	bfh.SetBfType()
	bfh.SetBfOffBits(uint32(unsafe.Sizeof(bfh) + unsafe.Sizeof(bi.BmiHeader)))
	bfh.SetBfSize(bfh.BfOffBits() + uint32(bmpSize))

	fo, _ := win.FileOpen("C:\\Temp\\foo.bmp", co.FILE_OPEN_RW_OPEN_OR_CREATE)
	defer fo.Close()

	fo.Write(bfh.Serialize())
	fo.Write(bi.BmiHeader.Serialize())
	fo.Write(bmpSlice)

	println("Done")
}

For reference, this example is now on GetDIBits documentation.

Friday, April 22, 2022

Generating React object keys with WeakMap

When iterating through an array in React, a key attribute is expected on the rendered elements, so a reordering is properly rendered. However, often the objects we’re rendering have no unique ID and using the plain index will give us a broken rendering when a reorder happens. So what should we use?

My first idea was to use the object itself as the key, but it must be a string or a number. Then, while researching the matter, I found a rather good solution: using a WeakMap object. I wasn’t even aware that such WeakMap existed, and turns out it’s perfect for the job.

A WeakMap is basically a Map which uses objects as keys. The difference from an ordinary Map is that the Map would retain the objects indefinitely – they would simply pile up, what can be seen as a memory leak –, while the WeakMap lets the objects being garbage collected when they are no longer referenced anywhere.

Given that concept imagine the following interface:

interface Person {
	name: string;
	age: number;
}

Now we have a React component which needs to render an array of Person. This is how we can write it:

interface Props {
	people: Person[];
}
	
function ThePeople(props: Props) {
	return <>
		{props.people.map(person =>
			<div key={getId(person)}>
				{person.name}, {person.age}
			</div>
		)}
	</>;
}

Note the getId function in the code above, which somewhat returns an unique ID for the object.

We’ll use a WeakMap to store the Person objects along with an auto-generated number, which will be its unique ID::

let currentId = 0;
let ids = new WeakMap<Object, number>();

export function getId(obj: Object): number {
	if (ids.has(obj)) {
		return ids.get(obj)!;
	} else {
		const newId = ++currentId;
		ids.set(obj, newId);
		return newId;
	}
}

For each object, the ID is set once, and it can be retrieved any number of times. This effectively eliminates the need of an alien _id field in our struct, and it also prevents the memory leaking of using an ordinary Map.

However, when using immutable state – which is basically the norm in React –, you’ll always have different objects, thus different IDs, and this will cause the loss of focus on elements. So, despite its ugliness, an _id attribute is still better. Or, if the list element won’t reorder, a simple index.

Thursday, April 7, 2022

VSCode extension Personal Access Token

I received an user request for my Format Comment VSCode extension, which pleases me greatly.

Implementing the feature itself took me just a few minutes, then I spent the next hour trying to figure out how to setup the “Personal Access Token” to publish the extension to the marketplace. So, to avoid this waste of time to my future self, these are the steps:

  1. Go to dev.azure.com/rodrigocfd to see the tokens;
  2. If necessary, create another one, which will be a long string;
  3. Organization must be set to “All Accessible Organizations”;
  4. On the project directory, run vsce login rodrigocfd and paste the long string when prompted.

The Personal Access Token lasts at max 1 year, so this process will have to be made at least once a year