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]"
}
}
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()); |
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)); |
/** | |
* 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>, |
@tailwind base; | |
@tailwind components; | |
@tailwind utilities; | |
@layer base { | |
html { | |
font-family: 'Rubik', system-ui, sans-serif; | |
} | |
} |
// 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 { |
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]"
}
}
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();