Skip to content

Instantly share code, notes, and snippets.

View rphlmr's full-sized avatar
Making things with Remix Run

Raphaël Moreau rphlmr

Making things with Remix Run
View GitHub Profile
@KristofferEriksson
KristofferEriksson / useCookie.ts
Created January 29, 2024 09:16
A hook to easily read and update browser cookies. Plus, it auto-updates your component when cookie values change
import { useEffect, useState } from "react";
type UseCookieReturnType = {
cookie: string | undefined;
setCookie: (value: string, days?: number) => void;
};
const useCookie = (cookieName: string): UseCookieReturnType => {
const getCookie = (name: string): string | undefined => {
const value = `; ${document.cookie}`;
@KristofferEriksson
KristofferEriksson / useMeasure.ts
Created January 27, 2024 16:52
A generic React TypeScript hook for measuring element dimensions in real-time
import { useEffect, useRef, useState } from "react";
interface MeasureResult<T extends Element> {
ref: React.RefObject<T>;
bounds: DOMRectReadOnly;
}
const useMeasure = <T extends Element = Element>(): MeasureResult<T> => {
const ref = useRef<T>(null);
const [bounds, setBounds] = useState<DOMRectReadOnly>(new DOMRectReadOnly());
@KristofferEriksson
KristofferEriksson / useLocalStorage.ts
Last active July 25, 2024 19:33
An easy-to-use API for storing and retrieving data from Local Storage in React, with built-in real-time synchronization
import { useEffect, useState } from "react";
function useLocalStorage() {
const [loadingStates, setLoadingStates] = useState<Map<string, boolean>>(
new Map()
);
const setStorageValue = <T>(key: string, value: T) => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
@KristofferEriksson
KristofferEriksson / useDynamicTextareaSize.ts
Last active April 9, 2024 10:49
A simple React hook to dynamically adjust the height of a textarea based on its content
/**
* Custom hook for dynamically resizing a textarea to fit its content.
* @param {React.RefObject<HTMLTextAreaElement>} textareaRef - Reference to the textarea element.
* @param {string} textContent - Current text content of the textarea.
* @param {number} maxHeight - Optional: maxHeight of the textarea in pixels.
*/
import { useEffect } from "react";
const useDynamicTextareaSize = (
textareaRef: React.RefObject<HTMLTextAreaElement>,
@Demandrel
Demandrel / style.css
Last active July 18, 2023 16:56
Style.css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
font-family: 'Rubik', system-ui, sans-serif;
}
}
@jordienr
jordienr / tailwind.config.ts
Created July 15, 2023 09:10
Tailwind SVG Grid Background
// Remember to install mini-svg-data-uri
// Follow me on twitter for memes @jordienr
import { type Config } from "tailwindcss";
const {
default: flattenColorPalette,
} = require("tailwindcss/lib/util/flattenColorPalette");
const svgToDataUri = require("mini-svg-data-uri");
export default {
@nandorojo
nandorojo / npm-rename.md
Last active May 4, 2024 23:15
How to rename an NPM package in your `package.json`

Add the package name you want to your package.json dependencies, and then make the value npm:<actual-package-name>. You can also add a version to the end.

package.json

{
  "dependencies": {
    "moti18": "npm:[email protected]"
  }
}
@kiliman
kiliman / index.tsx
Created February 7, 2023 21:31
Remix `useSubmitPromise` hook
import type { ActionArgs, LoaderArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import type { SubmitOptions } from "@remix-run/react";
import { useActionData, useNavigation, useSubmit } from "@remix-run/react";
import { useCallback, useEffect, useMemo } from "react";
export function loader({ request }: LoaderArgs) {
return json({ message: "Hello World" });
}
type InitFunction = (send: SendFunction) => CleanupFunction;
type SendFunction = (event: string, data: string) => void;
type CleanupFunction = () => void;
export function eventStream(request: Request, init: InitFunction) {
let stream = new ReadableStream({
start(controller) {
let encoder = new TextEncoder();
let send = (event: string, data: string) => {
controller.enqueue(encoder.encode(`event: ${event}\n`));

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();