Skip to content

Instantly share code, notes, and snippets.

View librz's full-sized avatar

Patrick Ren librz

View GitHub Profile
@librz
librz / use-window-control.ts
Created August 12, 2025 09:51
A failed attampt to controlling windows using React refs
// why this won't work:
// 1. ref is scoped to component
// 2. there's no way to know whether the window is already opened
import { useMemo, useRef } from "react";
export type WindowConfig = {
/** window path */
path: string;
@librz
librz / mock-data.js
Created August 7, 2025 06:54
Mock data that I often used
export const fruitOptions = [
{
value: "apple",
label: "Apple 🍎",
},
{
value: "grape",
label: "Grape 🍇",
},
{
@librz
librz / manual-form-submit.js
Created May 15, 2025 03:22
Prevent default form submit & manually submit using fetch
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const response = await fetch(form.action, {
method: form.method,
body: formData
});
// handle response...
});
@librz
librz / partial-video.tsx
Last active March 17, 2025 10:30
A react component to display partial video content
import { PauseIcon, PlayIcon } from "Todo";
import { Button } from "Todo";
import * as Slider from "@radix-ui/react-slider";
import classNames from "classnames";
import throttle from "lodash.throttle";
import {
useCallback,
useEffect,
useRef,
useState,
@librz
librz / VideoInfo.tsx
Created March 14, 2025 07:36
Get video size(in bytes) and duraiton(in seconds) on the frontend
import { useQuery } from "@tanstack/react-query";
import { useRef, useState } from "react";
async function fetchVideoContentLength(videoUrl: string) {
const controller = new AbortController();
const signal = controller.signal;
try {
const response = await fetch(videoUrl, {
// the idiomatic way of requesting only the response headers(without the content) is using a HEAD request, but server may not support the HEAD method
method: "GET",
@librz
librz / getErrorMessage.tsx
Created June 22, 2024 09:31
Get error message when an error is thrown
export function getErrorMessage(err: unknown, defaultMessage = "An error occurred") {
if (err instanceof Error && err.message) {
return err.message;
}
if (err instanceof Object && "message" in err && typeof err.message === "string" && err.message) {
return err.message;
}
if (typeof err === "string" && err) {
return err;
}
@librz
librz / index.md
Created April 16, 2024 11:51
`as const` except in js

In TypeScript, u can easily declare constants using as const:

const Meal = {
  Breakfast: 'breakfast',
  Lunch: 'lunch',
  Dinner: 'dinner'
}
@librz
librz / form-use-blocker.tsx
Last active April 11, 2024 12:43
React Router V6, Demo of prompt user for confirm when user navigate away or reload/close the page
import { useState } from "react";
import { useBeforeUnload, useBlocker } from "react-router-dom";
// unstable_usePrompt: https://reactrouter.com/en/main/hooks/use-prompt
// useBlocker: https://reactrouter.com/en/main/hooks/use-blocker
// useBeforeUnload: https://reactrouter.com/en/main/hooks/use-before-unload
export default function Form() {
const [input, setInput] = useState("");
@librz
librz / use-search-params.ts
Last active March 25, 2024 08:31
解决 react-router-dom v5 没有 useSearchParams 的问题
/**
* 以下代码来自 react-router-dom v6,用于解决 react-router-dom v5 没有 useSearchParams 的问题
* see: https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/index.tsx#L1432
* 在原代码的基础上做的改动:
* 1. 原代码使用了 useNavigate,但 react-router v5 还没有这个 hook,使用 useHistory 代替(https://reactrouter.com/en/main/upgrading/v5#use-usenavigate-instead-of-usehistory)
* 2. 在返回的数组中提供 updateSearchParams 以在保留其他参数的情况下设置/更新指定的参数
*/
import React, { useCallback } from 'react';
import { useLocation, useHistory } from 'react-router';
@librz
librz / groupByKeys.js
Created January 29, 2024 19:13
用 object 的 key 对 array of objects 进行分组
const list = [
{ province: '天津', city: '南开', district: '三马路' },
{
province: '天津',
city: '南开',
district: '四马路'
},
{
province: '天津',
city: '河西',