Skip to content

Instantly share code, notes, and snippets.

View samselikoff's full-sized avatar

Sam Selikoff samselikoff

View GitHub Profile
@samselikoff
samselikoff / settings.json
Created November 21, 2023 17:43
Tweaked Dracula theme for VSCode
{
"workbench.colorTheme": "Dracula",
"workbench.colorCustomizations": {
"terminal.ansiBrightRed": "#E06C75",
"terminal.ansiBrightBlue": "#528BFF",
"terminal.ansiBrightCyan": "#56B6C2",
"editorCursor.foreground": "#CBD6E8",
"editor.background": "#0f172a",
"menu.background": "#0f172a",
@samselikoff
samselikoff / git.diff
Created November 21, 2023 14:36
Diff from "Optimistic UI in Remix": https://www.youtube.com/watch?v=d0p95C3Kcsg
diff --git a/app/components/entry-form.tsx b/app/components/entry-form.tsx
index 50e5aeb..84c64fc 100644
--- a/app/components/entry-form.tsx
+++ b/app/components/entry-form.tsx
@@ -1,6 +1,6 @@
-import { useFetcher } from "@remix-run/react";
+import { Form, useSubmit } from "@remix-run/react";
import { format } from "date-fns";
-import { useEffect, useRef } from "react";
+import { useRef } from "react";
@samselikoff
samselikoff / settings.json
Created August 11, 2023 15:53
Dracula customizations using Tailwind Slate
{
"workbench.colorCustomizations": {
"editor.background": "#0f172a",
"menu.background": "#0f172a",
"sideBar.background": "#0b111e",
"banner.background": "#0f172a",
"tab.inactiveBackground": "#0b111e",
"tab.activeBackground": "#0f172a",
"titleBar.activeBackground": "#0b111e",
"editor.lineHighlightBorder": "#ff000000"
import { motion } from "framer-motion";
import { FormEvent, useState } from "react";
import { createGlobalState } from "react-hooks-global-state";
const { useGlobalState } = createGlobalState({
enabled: false,
delay: 1000,
});
@samselikoff
samselikoff / use-state.code-snippets
Last active July 31, 2023 16:19
A VSCode snippet that lets you type `ush` to define some new React state.
{
"Use state": {
"scope": "javascriptreact,typescriptreact",
"prefix": "ush",
"body": [
"let [${1}, set${1/(.*)/${1:/capitalize}/}] = useState($2);",
],
"description": "useState()"
}
}
@samselikoff
samselikoff / range.code-snippets
Last active May 29, 2023 16:28
VSCode snippet for creating a loop of n numbers in JSX! https://twitter.com/samselikoff/status/1611406980639244301
{
"Range of numbers": {
"scope": "javascriptreact,typescriptreact",
"prefix": "range",
"body": [
"{[...Array($1).keys()].map((i) => (",
" <$2 key={i}>",
" $3",
" </$2>",
"))}",

Remix's useFetcher doesn't return a Promise for any of its methods (like fetcher.submit()) because Remix doesn't want you to explicitly await anything so they can handle things like cancellation for you. Instead, they recommend adding a useEffect and performing whatever logic you need to after the fetcher is in a particular state.

I found using an effect to run some logic after a submission to be too indirect, and there seem to be plenty of cases where you want to submit a form and then perform some other work on the client (sometimes async, like requesting the user's permission for their location), and I'd rather just do that after a submission in the event handler rather than an effect.

So here's a proof of concept hook that wraps Remix's useFetcher and returns a version of submit that is a promise, and resolves with the data from the action:

function useFetcherWithPromise() {
  let resolveRef = useRef();
  let promiseRef = useRef();
@samselikoff
samselikoff / chart-with-d3.jsx
Last active April 19, 2024 01:11
Diff from "Building an Animated Line Chart with d3, React and Framer Motion" https://www.youtube.com/watch?v=kPbRDn5Fg0Y
import * as d3 from "d3";
import {
eachMonthOfInterval,
endOfMonth,
format,
isSameMonth,
parseISO,
startOfMonth,
} from "date-fns";
import useMeasure from "react-use-measure";
@samselikoff
samselikoff / 1-PictureForm.js
Last active June 8, 2023 02:04
Diff of the PictureForm component from "Let's build a feature – Cropped Image Uploads!" https://youtu.be/W5__zfYrtt8
import Button from "@/components/Button";
import ImageCropper, {
getCroppedImage,
getDataURLFromFile,
} from "@/components/ImageCropper";
import Modal from "@/components/Modal";
import useCurrentUser from "@/hooks/use-current-user";
import useMutation from "@/hooks/use-mutation";
import { UserIcon } from "@heroicons/react/outline";
import { gql } from "graphql-request";
import { isTest } from "@/lib/constants";
import { Dialog } from "@headlessui/react";
import { motion } from "framer-motion";
const TRANSITIONS = {
DURATION: !isTest ? 0.5 : 0,
EASE: [0.32, 0.72, 0, 1],
};
function Modal({ onClose = () => {}, initialFocusRef, children }) {