| Navigates? | declarative? | Makes GET, triggers loader | Makes POST, triggers action | No requests |
|---|---|---|---|---|
| navigates | declarative | <Link to=""><Form method="get"> |
<Form method="post"> |
<Link to="#..."> |
| navigates | imperative | navigate()setSearchParams() |
submit() |
navigate("#") |
| stays | declarative | <fetcher.Form method="get"> |
<fetcher.Form method="post"> |
(doesn't make sense) |
| s |
| /** | |
| * Fancy ID generator that creates 20-character string identifiers with the following properties: | |
| * | |
| * 1. They're based on timestamp so that they sort *after* any existing ids. | |
| * 2. They contain 72-bits of random data after the timestamp so that IDs won't collide with other clients' IDs. | |
| * 3. They sort *lexicographically* (so the timestamp is converted to characters that will sort properly). | |
| * 4. They're monotonically increasing. Even if you generate more than one in the same timestamp, the | |
| * latter ones will sort after the former ones. We do this by using the previous random bits | |
| * but "incrementing" them by 1 (only in the case of a timestamp collision). | |
| */ |
| --- | |
| description: "Use shadcn/ui components as needed for any UI code" | |
| patterns: "*.tsx" | |
| --- | |
| # Shadcn UI Components | |
| This project uses @shadcn/ui for UI components. These are beautifully designed, accessible components that you can copy and paste into your apps. | |
| ## Finding and Using Components |
| // Zero-Clause BSD (more permissive than MIT, doesn't require copyright notice) | |
| // | |
| // Permission to use, copy, modify, and/or distribute this software for any purpose | |
| // with or without fee is hereby granted. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | |
| // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | |
| // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS | |
| // OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| // TODO: make `pages` optional and measure the div when unspecified, this will | |
| // allow more normal document flow and make it easier to do both mobile and | |
| // desktop. | |
| import { | |
| createContext, | |
| useCallback, | |
| useContext, | |
| useEffect, | |
| useMemo, | |
| useRef, |
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();| import Stripe from 'stripe' | |
| const stripe = new Stripe(process.env.STRIPE_SECRET_KEY) | |
| export const action = async ({request}) => { | |
| const secret = 'whsec_...' // process.env.WEBHOOK_SIGNING_SECRET | |
| const sig = request.headers.get('stripe-signature') | |
| let event; | |
| const payload = await request.text() |
| import { getConfig } from '@expo/config' | |
| import fs from 'fs' | |
| import spawnAsync from '@expo/spawn-async' | |
| import chalk from 'chalk' | |
| import path from 'path' | |
| const appDir = process.cwd() | |
| console.log() | |
| console.log(chalk.green('Sentry source maps script. Working directory:')) |
This recipe revisions all asset files in a dist/assets/ directory using gulp-rev.
The adds a unique content based hash to each asset file.
The pattern of that hash (/.*-[0-9a-f]{10}\..*/) is then used to handle these files in Express.js and the Service Worker.
Express.js sets the Cache-Control header to immutable.
The Service Worker serves these files directly from cache without ever attempting the server again.