Skip to content

Instantly share code, notes, and snippets.

View simonrelet's full-sized avatar
🏠
Working from home

Simon Relet simonrelet

🏠
Working from home
  • Paris
View GitHub Profile
@simonrelet
simonrelet / useFetch.js
Last active June 4, 2021 22:27
React Request's Fetch component transposed in hooks
import { fetchDedupe, getRequestKey, isRequestInFlight } from 'fetch-dedupe'
import { useEffect, useRef, useState } from 'react'
// This in the Fetch component transposed in hooks.
// https://github.com/jamesplease/react-request/issues/199
// https://github.com/jamesplease/react-request/blob/master/src/fetch.js
// This object is our cache.
// The keys of the object are requestKeys.
// The value of each key is a Response instance.
@simonrelet
simonrelet / asynchronous.js
Last active March 18, 2023 20:13
React hooks for asynchronous calls
import React from 'react'
/**
* @typedef {object} State The state of asynchronous hooks.
* @property {object | null} error The error.
* @property {boolean} pending Whether the call is pending.
* @property {any | null} result The result of the asynchronous call.
*/
/** @type {State} */
@simonrelet
simonrelet / apiMappers.js
Last active May 2, 2019 17:00
Bidirectional object mapping
/**
* @typedef {object} Mapper
* @property {function(any): any} fromAPI
* @property {function(any): any} toAPI
*
* @typedef {object} Config
* @property {string} [name]
* @property {Mapper} [mapper]
*
* @typedef {Object.<string, string | Config>} MixedMappings
/** Geohashes are in base32. */
const MAX_CHILDREN_COUNT = 32
/**
* Compress an array of Geohashes using Hash Tree compression.
*
* @param {string[]} geohashes Array of Geohashes.
* @param {number} [minChildrenCount=32] The minimum number of children to concider a node full.
* @returns {string[]} A compressed version of the Geohashes.
*/
import invariant from 'invariant'
import warning from 'warning'
function identity(state) {
return state
}
function parse(serializedJSON) {
if (serializedJSON) {
try {
import throttle from 'lodash.throttle'
const DEFAULT_HISTORY_CAPACITY = 50
const DEFAULT_PUSH_THROTTLE_TIMEOUT = 500
/**
* The object defining the internal state of a History returned by
* `History.getState`.
*
* @template T
@simonrelet
simonrelet / types_and_values.md
Created January 21, 2022 13:53
Types and Values

Extracted from https://stackoverflow.com/a/50396312

TypeScript: A world of Types and Values

As you are likely aware, TypeScript adds a static type system to JavaScript, and that type system gets erased when the code is transpiled. The syntax of TypeScript is such that some expressions and statements refer to values that exist at runtime, while other expressions and statements refer to types that exist only at design/compile time. Values have types, but they are not types themselves. Importantly, there are some places in the code where the compiler will expect a value and interpret the expression it finds as a value if possible, and other places where the compiler will expect a type and interpret the expression it finds as a type if possible.

The compiler does not care or get confused if it is possible for an expression to be interpreted as both a value and a type.

@simonrelet
simonrelet / index.page.tsx
Created February 3, 2023 18:17
URL query validation and typing with NextJs and Zod
// Shared URL query that can be imported from anywhere.
const PaginationUrlQuerySchema = z.object({
// Use .catch() in order to return a default value when validation fails.
// See https://zod.dev/?id=catch
page: z.coerce.number().min(0).catch(0),
});
// Page specific URL query combined with shared ones.
const MyPageUrlQuerySchema = PaginationUrlQuerySchema.extend({
enum: z.enum(["foo", "bar"]).catch("foo"),
import { useMemo } from "react";
export function useComposedRef<TElement>(handlers: RefHandler<TElement>[]) {
// We actually want the handlers to be the dependency list.
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(() => composeRefs(handlers), handlers);
}
function composeRefs<TElement>(
handlers: RefHandler<TElement>[],
import { useSearchParams } from "@remix-run/react";
import isEqual from "lodash.isequal";
import { useCallback, useMemo } from "react";
import type { NavigateOptions } from "react-router";
export function useParsedSearchParams<
TOutput extends object,
TInput extends object,
>(delegate: URLSearchParamsDelegate<TOutput, TInput>) {
const [searchParams, setSearchParams] = useSearchParams();