Skip to content

Instantly share code, notes, and snippets.

View dr-skot's full-sized avatar

Scott Shepherd dr-skot

View GitHub Profile
@dr-skot
dr-skot / mysql-timestamps
Created March 14, 2023 16:29
MySQL CREATE TABLE syntax for automatic creation-time and modification-time columns
CREATE TABLE `database-name`.`table-name`(
...
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB;
@dr-skot
dr-skot / circular-json.test.js
Last active February 28, 2023 18:56
Serialize and deserialize circular objects to/from JSON
import { fromJSON, toJSON } from ./circular-json';
describe('toJSON', () => {
it('handles ordinary objects', () => {
const inputs = [
null,
{ a: 1, b: { c: 3 }, d: [4, 5, 6], e: undefined },
[1, 2, 3],
1,
'a',
@dr-skot
dr-skot / ref-vs-state.tsx
Created February 19, 2023 03:53
React component demonstrating the difference between useRef and useState, and its implications
import { useRef, useState } from 'react';
import { Box, Button, HStack } from '@chakra-ui/react';
export default function RefVsState() {
const [state, setState] = useState(0);
const ref = useRef(0);
console.info('----RENDER----');
return (
@dr-skot
dr-skot / addInTimeZone.test.ts
Last active February 6, 2023 17:59
addInTimeZone: timezone-aware version of the date-fns `add` function
import addInTimeZone from './addInTimeZone';
import { formatInTimeZone } from 'date-fns-tz';
it('adds days + hours to a date, with timezone awareness', () => {
// add a day plus 3 hours to this time, and you'll cross a DST boundary
// in both Halifax and LA
const start = '2022-11-05T06:12:34.567Z';
const startTime = new Date(start).getTime();
// but in Halifax you cross it adding the day; in LA you cross it adding hours
// i.e. in Halifax you add a 25-hr day plus 3 hrs = 28 hrs, and clock time is +3 hrs
@dr-skot
dr-skot / InsertButton.tsx
Last active August 31, 2022 17:47
A Chakra button that inserts text into an input element
import { Button, ButtonProps } from '@chakra-ui/react';
import { RefObject } from 'react';
export type InsertableElement = HTMLInputElement | HTMLTextAreaElement;
function insertStringAtCursor(target: InsertableElement, insertion: string) {
const value = target.value;
const selectionStart = target.selectionStart ?? value.length;
const selectionEnd = target.selectionEnd ?? selectionStart;
const start = value.substring(0, selectionStart);
@dr-skot
dr-skot / onNonTouchMouseMove.ts
Last active July 31, 2022 18:10
Suppress synthetic MouseMove event on touch
// touch triggers both TouchStart and MouseMove events
// and not in a bubbling way -- both are thrown no matter how much you preventDefault()
// this suppresses the synthetic MouseMove event on touch
function onNonTouchMouseMove(mouseMoveHandler: MouseEventHandler<unknown>) {
let ignoreMouseMove = false;
return {
onTouchStart: () => {
ignoreMouseMove = true;
},
onMouseMove: (event: MouseEvent<unknown>) => {
@dr-skot
dr-skot / useRefWithRerender.ts
Last active July 31, 2022 17:57
Sometimes you want both useRef's never-stale guarantee and useState's rerender-on-change functionality. This hook delivers a ref & a setter that triggers rerender on change.
function useRefWithRerender<T>(initialState: T): [MutableRefObject<T>, (value: T) => void] {
const ref = useRef(initialState);
const [, setState] = useState(true);
const setter = useCallback((value: T) => {
if (ref.current === value) return;
ref.current = value;
setState((n) => !n);
}, []);
@dr-skot
dr-skot / createStoredSignal.ts
Last active July 30, 2022 14:33
A SolidJS signal that maintains persistence in local or session storage.
import { createSignal, Signal } from "solid-js";
export function createStoredSignal<T>(key: string, defaultValue: T, storage = localStorage): Signal<T> {
const initialValue = tryToParse(storage.getItem(key), defaultValue);
const [value, setValue] = createSignal<T>(initialValue);
const setValueAndStore = ((arg) => {
const v = setValue(arg);
storage.setItem(key, JSON.stringify(v));
return v;
@dr-skot
dr-skot / Parse.ts
Last active May 16, 2022 12:17
Utilities for using the Parse Platform with nextjs. Prefer this to @parse/react-ssr
import BrowserParse from 'parse';
import ServerParse from 'parse/node';
import _ from 'lodash';
const isBrowser = !!global.window;
const Parse = isBrowser ? BrowserParse : ServerParse;
export default Parse;
export function initialize(serverURL: string, applicationId: string, javascriptKey: string): void {
Parse.serverURL = serverURL;
@dr-skot
dr-skot / useEffectOnMount.ts
Last active July 31, 2022 18:13
run an effect when the dependencies change, but not at initialization
import { useEffect, useRef, EffectCallback, DependencyList } from 'react';
// React.useEffect runs its effect 1) at initialization and then 2) whenever the deps change
// Sometimes you don't want (1), just (2). That's what this is for.
export function useEffectOnMount(effect: EffectCallback, deps: DependencyList): void {
const isMounted = useRef(false);
useEffect(() => {
if (isMounted.current) return effect();