GithubHelp home page GithubHelp logo

rudyhuynh / use-url-search-params Goto Github PK

View Code? Open in Web Editor NEW
62.0 3.0 6.0 5.71 MB

A React Hook to use URL query string as a state management

License: MIT License

HTML 10.58% CSS 5.86% JavaScript 33.41% Shell 0.08% TypeScript 50.08%
react react-hooks urlsearchparams url-query

use-url-search-params's People

Contributors

brynetane avatar rudyhuynh avatar vincentdesmares avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

use-url-search-params's Issues

Browser history is doubled

Hi,

recently I have started using this very useful hook but found a strange behavior when using the browser's back button.
When you modify your params state several times and then you try to navigate back using the browser back button, only every second attempt is correct, I mean reflected in the application.
I created a simple demo to show the behavior https://codesandbox.io/s/delicate-lake-8todi?file=/src/App.js:915-932.

Steps to reproduce:

  1. type some phrase into textfield
  2. hit enter / click submit button
  3. type a different phrase
  4. hit enter / click submit button
  5. use back button in emulated browser tab

You have to click twice to navigate back to previous phrase / state of URL.

Is this intention?

A wrapper with Typescript

Here is a Typescript wrapper around this package I write for my project (with a very good type-inference). The API has changed a little bit (initial and types are now combined into 1 param called initialOrType for more concise syntax).

Usage

const [params, setParams] = useUrlSearchParams({ name: 'loi', age: Number, handsome: true }); // see value ('loi') and type (Number) are mixed

Now, the initial URLSearchParams is ?name=loi&handsome=true, with the type of params to be

{ 
  name?: string, 
  age?: number, 
  handsome?: boolean 
}

Or, if you want to take full advantage of typescript to narrow the type:

type Params = { name:'loi' | 'Loi', age: number, handsome: true }
const [params, setParams] = useUrlSearchParams({ name: 'loi', age: 21, handsome: true } as Params);

Now the type of params becomes

{ 
  name?: 'loi' | 'Loi' /* instead of string */, 
  age?: number, 
  handsome?: true /* instead of boolean */ 
}

Or you can mix initial params with types (in case not all types have initial params) and still narrow the type:

const [params, setParams] = useUrlSearchParams({
  name: 'loi' as 'loi | 'Loi',
  age: Number,
  handsome: true as true
});

will result in the same type as the example above.

File

(I will update immediately here if there's any bug)

useUrlSearchParams.ts

import { useUrlSearchParams as useOriginal } from 'use-url-search-params';

/**
 * mix of Constructor and initial params
 * e.g: TProps { a: '1', b: Number } will result to:
 * - type typescript of { a: string, b: number }
 * - type check (second param of useOriginal) of { a: String, b: Number }
 * - initial params of { a: '1' } or ?a=1
 */
type TProps = Readonly<{
  [key: string]:
    | NumberConstructor
    | StringConstructor
    | BooleanConstructor
    | DateConstructor
    | number
    | string
    | boolean
    | Date;
  // See all the types supported by use-url-search-params here:
  // https://github.com/rudyhuynh/use-url-search-params/blob/95b0880bf9e2bf84e19c779b2a4078a47271ea9c/src/useUrlSearchParams.js#L4
}>;

/**
 * Convert from TProps to typescript type of the params
 * e.g: TProps { a: '1', b: Number } will result to:
 * { a: string, b: number }
 */
type GetUrlSearchParams<T extends TProps> = {
  // We have the ? (optional) here because URLSearchParams is an external thing, so we cannot control whether it is presented as we expected it to be
  [K in keyof T]?: T[K] extends NumberConstructor
    ? number
    : T[K] extends StringConstructor
    ? string
    : T[K] extends BooleanConstructor
    ? boolean
    : T[K] extends DateConstructor
    ? Date
    : T[K];
};

/**
 *
 * @param value mixed of initial param values and Constructors
 * @returns whether the "value" is just a value, not a Constructor
 * e.g: '1' => true, Number => false, String => false
 */
const isNotConstructor = (
  value: TProps[string],
): value is string | number | boolean | Date =>
  typeof value === 'string' ||
  typeof value === 'number' ||
  typeof value === 'boolean' ||
  value instanceof Date;

/**
 * wrapper around the original "useUrlSearchParams":
 * https://github.com/rudyhuynh/use-url-search-params
 */
export function useUrlSearchParams<Props extends TProps>(initialOrType: Props) {
  // exptract "initial" from "initialOrType"
  // desired result: { param1: 'value1', param2: 123456 }
  const initParams = Object.fromEntries(
    // Learn the Object.fromEntries/Object.entries combination here:
    // https://stackoverflow.com/a/14810722/9787887
    Object.entries(initialOrType).filter(([_, value]) =>
      isNotConstructor(value),
    ),
  );

  // exptract "type" from "initialOrType"
  // desired result: { param1: String, param2: Number }
  const paramsTypes = Object.fromEntries(
    Object.entries(initialOrType).map(([key, value]) => [
      key,
      isNotConstructor(value) ? value.constructor : value,
    ]),
  );

  const [params, setParams] = useOriginal(initParams, paramsTypes);

  // The setParams will add params to current params, NOT flush out all current params
  // E.g: ?a=1&b=2, setParams({a:3}) will result in ?a=3&b=2
  // look at its implementation here:
  // https://github.com/rudyhuynh/use-url-search-params/blob/95b0880bf9e2bf84e19c779b2a4078a47271ea9c/src/useUrlSearchParams.js#L11
  return [params, setParams] as unknown as [
    GetUrlSearchParams<Props>,
    (newParams: GetUrlSearchParams<Props>) => void,
  ];
}

add Date support by default

First of all, thank you for creating this library! It looks quite nice.
Can you just add a support for Date out of the box? I know I could write a custom resolver function, but this being a native JS primitive I think it deserves to be supported without manualy resolvers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.