Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ import {
} from 'use-query-params';

const UseQueryParamsExample = () => {
// something like: ?x=123&q=foo&filters=a_b_c in the URL
// something like: ?x=123&q=foo&filters=a&filters=b&filters=c in the URL
const [query, setQuery] = useQueryParams({
x: NumberParam,
q: StringParam,
Expand Down Expand Up @@ -153,17 +153,20 @@ See [all param definitions here](https://github.com/pbeshai/use-query-params/blo

Note that all nully values will encode and decode as `undefined`.

Examples in this table assume query parameter named `qp`.

| Param | Type | Example Decoded | Example Encoded |
| --- | --- | --- | --- |
| StringParam | string | `'foo'` | `'foo'` |
| NumberParam | number | `123` | `'123'` |
| ObjectParam | { key: string } | `{ foo: 'bar', baz: 'zzz' }` | `'foo-bar_baz-zzz'` |
| ArrayParam | string[] | `['a','b','c']` | `'a_b_c'` |
| JsonParam | any | `{ foo: 'bar' }` | `'{"foo":"bar"}'` |
| DateParam | Date | `Date(2019, 2, 1)` | `'2019-03-01'` |
| BooleanParam | boolean | `true` | `'1'` |
| NumericObjectParam | { key: number } | `{ foo: 1, bar: 2 }` | `'foo-1_bar-2'` |
| NumericArrayParam | number[] | `[1, 2, 3]` | `'1_2_3'` |
| StringParam | string | `'foo'` | `?qp=foo` |
| NumberParam | number | `123` | `?qp=123` |
| ObjectParam | { key: string } | `{ foo: 'bar', baz: 'zzz' }` | `?qp=foo-bar_baz-zzz` |
| ArrayParam | string[] | `['a','b','c']` | `?qp=a&qp=b&qp=c` |
| JsonParam | any | `{ foo: 'bar' }` | `?qp=%7B%22foo%22%3A%22bar%22%7D` |
| DateParam | Date | `Date(2019, 2, 1)` | `?qp=2019-03-01` |
| BooleanParam | boolean | `true` | `?qp=1` |
| NumericObjectParam | { key: number } | `{ foo: 1, bar: 2 }` | `?qp=foo-1_bar-2` |
| DelimitedArrayParam | string[] | `['a','b','c']` | `?qp=a_b_c'` |
| DelimitedNumericArrayParam | number[] | `[1, 2, 3]` | `?qp=1_2_3'` |

**Example**

Expand All @@ -176,6 +179,26 @@ const [foo, setFoo] = useQueryParam('foo', ArrayParam);
const [query, setQuery] = useQueryParams({ foo: ArrayParam });
```

**Example with Custom Param**

You can define your own params if the ones shipped with this package don't work for you. There are included [serialization utility functions](https://github.com/pbeshai/use-query-params/blob/master/src/serialize.ts) to make this easier, but you can use whatever you like.

```js
import {
encodeDelimitedArray,
decodeDelimitedArray
} from 'use-query-params';

/** Uses a comma to delimit entries. e.g. ['a', 'b'] => qp?=a,b */
const CommaArrayParam = {
encode: (array: string[] | null | undefined) =>
encodeDelimitedArray(array, ','),

decode: (arrayStr: string | string[] | null | undefined) =>
decodeDelimitedArray(arrayStr, ',')
};
```

<br/>


Expand Down
6 changes: 4 additions & 2 deletions examples/react-router/src/UseQueryParamExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import {

const MyParam = {
encode: (val: number) => `MY_${val}`,
decode: (str: string | undefined) =>
str == null ? undefined : +str.split('_')[1],
decode: (input: string | string[] | undefined) => {
const str = input instanceof Array ? input[0] : input;
return str == null ? undefined : +str.split('_')[1];
},
};

const UseQueryParamExample = () => {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"dev": "NODE_ENV=development tsc -w",
"test": "jest",
"test-watch": "jest --watch",
"test-coverage": "jest --coverage"
"test-coverage": "jest --coverage",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
Expand Down
22 changes: 16 additions & 6 deletions src/__tests__/params-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import {
NumberParam,
ObjectParam,
ArrayParam,
NumericArrayParam,
JsonParam,
DateParam,
BooleanParam,
NumericObjectParam,
NumericArrayParam,
DelimitedArrayParam,
DelimitedNumericArrayParam,
} from '../index';

describe('params', () => {
Expand All @@ -25,8 +27,12 @@ describe('params', () => {
expect(ObjectParam.decode('foo-bar')).toEqual({ foo: 'bar' });
});
it('ArrayParam', () => {
expect(ArrayParam.encode(['foo', 'bar'])).toBe('foo_bar');
expect(ArrayParam.decode('foo_bar')).toEqual(['foo', 'bar']);
expect(ArrayParam.encode(['foo', 'bar'])).toEqual(['foo', 'bar']);
expect(ArrayParam.decode(['foo', 'bar'])).toEqual(['foo', 'bar']);
});
it('NumericArrayParam', () => {
expect(NumericArrayParam.encode([1, 2])).toEqual(['1', '2']);
expect(NumericArrayParam.decode(['1', '2'])).toEqual([1, 2]);
});
it('JsonParam', () => {
expect(JsonParam.encode({ foo: 'bar' })).toBe('{"foo":"bar"}');
Expand All @@ -47,9 +53,13 @@ describe('params', () => {
expect(NumericObjectParam.encode({ foo: 123 })).toBe('foo-123');
expect(NumericObjectParam.decode('foo-123')).toEqual({ foo: 123 });
});
it('NumericArrayParam', () => {
expect(NumericArrayParam.encode([1, 2])).toBe('1_2');
expect(NumericArrayParam.decode('1_2')).toEqual([1, 2]);
it('DelimitedArrayParam', () => {
expect(DelimitedArrayParam.encode(['foo', 'bar'])).toBe('foo_bar');
expect(DelimitedArrayParam.decode('foo_bar')).toEqual(['foo', 'bar']);
});
it('DelimitedNumericArrayParam', () => {
expect(DelimitedNumericArrayParam.encode([1, 2])).toBe('1_2');
expect(DelimitedNumericArrayParam.decode('1_2')).toEqual([1, 2]);
});
});
});
120 changes: 107 additions & 13 deletions src/__tests__/serialize-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ import {
decodeJson,
encodeArray,
decodeArray,
encodeNumericArray,
decodeNumericArray,
encodeObject,
decodeObject,
encodeNumericObject,
encodeNumericArray,
decodeNumericObject,
decodeNumericArray,
encodeDelimitedNumericArray,
decodeDelimitedNumericArray,
encodeDelimitedArray,
decodeDelimitedArray,
} from '../index';

describe('serialize', () => {
Expand Down Expand Up @@ -60,6 +64,13 @@ describe('serialize', () => {
const result = decodeDate('foo-one-two');
expect(result).not.toBeDefined();
});

it('handles array of values', () => {
const result = decodeDate(['2019-03-01', '2019-05-01']);
expect(result.getFullYear()).toBe(2019);
expect(result.getMonth()).toBe(2);
expect(result.getDate()).toBe(1);
});
});

describe('encodeBoolean', () => {
Expand All @@ -81,6 +92,10 @@ describe('serialize', () => {
it('handles malformed input', () => {
expect(decodeBoolean('foo')).not.toBeDefined();
});

it('handles array of values', () => {
expect(decodeBoolean(['1', '0'])).toBe(true);
});
});

describe('encodeNumber', () => {
Expand All @@ -102,6 +117,10 @@ describe('serialize', () => {
it('handles malformed input', () => {
expect(decodeNumber('foo')).not.toBeDefined();
});

it('handles array of values', () => {
expect(decodeNumber(['1', '0'])).toBe(1);
});
});

describe('encodeString', () => {
Expand All @@ -118,6 +137,10 @@ describe('serialize', () => {
expect(decodeString(undefined)).not.toBeDefined();
expect(decodeString(null)).not.toBeDefined();
});

it('handles array of values', () => {
expect(decodeString(['foo', 'bar'])).toBe('foo');
});
});

describe('encodeJson', () => {
Expand Down Expand Up @@ -146,19 +169,23 @@ describe('serialize', () => {
it('handles malformed input', () => {
expect(decodeJson('foo')).not.toBeDefined();
});

it('handles array of values', () => {
expect(decodeJson(['"foo"', '"bar"'])).toBe('foo');
});
});

describe('encodeArray', () => {
it('produces the correct value', () => {
const input = ['a', 'b', 'c'];
expect(encodeArray(input)).toBe('a_b_c');
expect(encodeArray(input)).toEqual(['a', 'b', 'c']);
expect(encodeArray(undefined)).not.toBeDefined();
});
});

describe('decodeArray', () => {
it('produces the correct value', () => {
const output = decodeArray('a_b_c');
const output = decodeArray(['a', 'b', 'c']);
const expectedOutput = ['a', 'b', 'c'];

expect(output).toEqual(expectedOutput);
Expand All @@ -167,7 +194,30 @@ describe('serialize', () => {
});

it('filters empty values', () => {
expect(decodeArray('__')).toEqual([]);
expect(decodeArray(['a', '', 'b'])).toEqual(['a', 'b']);
});
});

describe('encodeNumericArray', () => {
it('produces the correct value', () => {
const input = [1, 2, 3];
expect(encodeNumericArray(input)).toEqual(['1', '2', '3']);
expect(encodeNumericArray(undefined)).not.toBeDefined();
});
});

describe('decodeNumericArray', () => {
it('produces the correct value', () => {
const output = decodeNumericArray(['1', '2', '3']);
const expectedOutput = [1, 2, 3];

expect(output).toEqual(expectedOutput);
expect(decodeNumericArray(undefined)).not.toBeDefined();
expect(decodeNumericArray('')).not.toBeDefined();
});

it('filters empty and NaN values', () => {
expect(decodeNumericArray(['1', '', '2', 'foo', '3'])).toEqual([1, 2, 3]);
});
});

Expand Down Expand Up @@ -203,28 +253,66 @@ describe('serialize', () => {
grill: undefined,
});
});

it('handles array of values', () => {
expect(decodeObject(['foo-bar', 'jim-grill'])).toEqual({ foo: 'bar' });
});
});

describe('encodeNumericArray', () => {
describe('encodeDelimitedArray', () => {
it('produces the correct value', () => {
const input = ['a', 'b', 'c'];
expect(encodeDelimitedArray(input)).toBe('a_b_c');
expect(encodeDelimitedArray(undefined)).not.toBeDefined();
});
});

describe('decodeDelimitedArray', () => {
it('produces the correct value', () => {
const output = decodeDelimitedArray('a_b_c');
const expectedOutput = ['a', 'b', 'c'];

expect(output).toEqual(expectedOutput);
expect(decodeDelimitedArray(undefined)).not.toBeDefined();
expect(decodeDelimitedArray('')).not.toBeDefined();
});

it('filters empty values', () => {
expect(decodeDelimitedArray('__')).toEqual([]);
});

it('handles array of values', () => {
expect(decodeDelimitedArray(['foo_bar', 'jim_grill'])).toEqual([
'foo',
'bar',
]);
});
});

describe('encodeDelimitedNumericArray', () => {
it('produces the correct value', () => {
const input = [9, 4, 0];
expect(encodeNumericArray(input)).toBe('9_4_0');
expect(encodeNumericArray(undefined)).not.toBeDefined();
expect(encodeDelimitedNumericArray(input)).toBe('9_4_0');
expect(encodeDelimitedNumericArray(undefined)).not.toBeDefined();
});
});

describe('decodeNumericArray', () => {
describe('decodeDelimitedNumericArray', () => {
it('produces the correct value', () => {
const output = decodeNumericArray('9_4_0');
const output = decodeDelimitedNumericArray('9_4_0');
const expectedOutput = [9, 4, 0];

expect(output).toEqual(expectedOutput);
expect(decodeNumericArray(undefined)).not.toBeDefined();
expect(decodeNumericArray('')).not.toBeDefined();
expect(decodeDelimitedNumericArray(undefined)).not.toBeDefined();
expect(decodeDelimitedNumericArray('')).not.toBeDefined();
});

it('filters empty values', () => {
expect(decodeNumericArray('__')).toEqual([]);
expect(decodeDelimitedNumericArray('__')).toEqual([]);
});

it('handles array of values', () => {
expect(decodeDelimitedNumericArray(['1_2', '3_4'])).toEqual([1, 2]);
});
});

Expand Down Expand Up @@ -262,5 +350,11 @@ describe('serialize', () => {
grill: undefined,
});
});

it('handles array of values', () => {
expect(decodeNumericObject(['foo-55', 'jim-100'])).toEqual({
foo: 55,
});
});
});
});
Loading