Skip to content

Instantly share code, notes, and snippets.

View karol-majewski's full-sized avatar

Karol Majewski karol-majewski

View GitHub Profile
@karol-majewski
karol-majewski / useDeliveryAddress.ts
Created February 21, 2025 12:08
Fetching Next.js routes with Suspense
import { use } from "react";
import type { DeliveryAddress } from "./types";
const cache = new Map<string, Promise<DeliveryAddress>>();
export function useDeliveryAddress(userId: string): DeliveryAddress {
if (!cache.has(userId)) {
const promise = fetch(`/api/user/delivery-address/${userId}`).then(
(response) => {
if (!response.ok) {
@karol-majewski
karol-majewski / usePrivyUser.ts
Last active February 14, 2025 14:11
Accessing Privy users with Suspense
import { use } from 'react';
import { usePrivy, User } from '@privy-io/react-auth';
const RETRY_INTERVAL = 50; // ms
// Move promise creation outside the component
let authPromise: Promise<User> | null = null;
function createAuthPromise(ready: boolean, authenticated: boolean, user: User | null) {
return new Promise<User>((resolve, reject) => {
@karol-majewski
karol-majewski / useAddress.ts
Created February 13, 2025 14:11
Using Suspense with a callback-based API
const cache = new Map<string, Promise<google.maps.places.PlaceResult>>();
export function useAddress(placeId: string) {
const getPlacesService = useCallback(() => {
return new google.maps.places.PlacesService(document.createElement("div"));
}, []);
// Check if we already have a cached promise for this placeId
if (!cache.has(placeId)) {
const placesService = getPlacesService();
@karol-majewski
karol-majewski / tuple-operations.ts
Created February 29, 2024 16:13
Preserving tuple length
// #region Necessary types
type NonEmptyArray<T> = [head: T, ...T[]];
type Tuple<T> = [T, T];
declare function head<T>(collection: [T, ...T[]]): T;
// #endregion
const cloudflareDNS: NonEmptyArray<string> = ["1.1.1.1"];
const googleDNS: Tuple<string> = ["8.8.8.8", "8.8.4.4"]
// ✅ These Just Work™️
@karol-majewski
karol-majewski / promisify.ts
Created December 29, 2023 13:36
Convert synchronous methods to async methods
type PromisifyMethods<T> = {
[K in keyof T]: T[K] extends AnyFunction ? (...parameters: Parameters<T[K]>) => Promise<ReturnType<T[K]>> : T[K];
};
type AsynchronousStorage = PromisifyMethods<Storage>;
export function promisify(storage: Storage): AsynchronousStorage {
return new Proxy(storage, {
get(target, name: string) {
if (typeof target[name] === 'function') {
@karol-majewski
karol-majewski / getStyles.ts
Created September 28, 2023 09:52
Function composition
import { FlattenSimpleInterpolation } from 'styled-components';
import { pick } from './objects';
type Stylesheet<K extends string> = Record<K, FlattenSimpleInterpolation>;
/**
* CSS-in-JS equivalent for `classNames`.
*
* @example
interface Response<T> {
status: 'success' | 'pending' | 'error';
data: T | null;
}
/**
* A promise tracker that will be updated
* when promise resolves or rejects
*/
const response: Response<unknown> = {
import * as React from 'react';
const NON_BREAKING_SPACE_CHARACTER = '\u00A0';
interface Props {
children: string;
/**
* If the text is shorter than this, it will not be processed.
*/
requiredLength?: number;
import React from 'react';
import createPersistedState from 'use-persisted-state';
import { storage } from '../utilities/storage';
import { hasElements, head, isDefined } from '@rhim/utils';
const usePersistedEntityId = createPersistedState('some-entity-key', storage);
@karol-majewski
karol-majewski / bound.ts
Last active March 30, 2023 13:12
Decorator that binds class methods compatible with TypeScript 5.0
function bound<This extends object, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
const methodName = String(context.name);
if (context.private) {
throw new Error(`${bound.name} cannot decorate private properties like ${methodName}.`);
}