Skip to content

Commit

Permalink
fix: minor refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
asmyshlyaev177 committed Nov 7, 2024
1 parent 7fee67f commit 7a10939
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 75 deletions.
19 changes: 2 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 26 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,6 @@
"version": "4.0.3",
"description": "Easily share complex state objects between unrelated React components, preserve types and structure, with TS validation. Deep links and url state synchronization wthout any hasssle or boilerplate.",
"homepage": "https://state-in-url.dev",
"keywords": [
"front-end",
"state-management",
"state management",
"deep links",
"deep linking",
"url synchronization",
"useUrlState",
"state in url",
"client components communication",
"query string",
"search params",
"query params",
"typescript",
"workflow",
"javascript",
"hooks",
"react.js",
"reactjs",
"react",
"NextJS",
"Next.js",
"nuqs alternative",
"query params parser",
"query params parsing"
],
"repository": {
"type": "git",
"url": "git+https://github.com/asmyshlyaev177/state-in-url.git"
Expand Down Expand Up @@ -404,5 +378,31 @@
"wait-on": "^8.0.1",
"wireit": "^0.14.9"
},
"keywords": [
"front-end",
"state-management",
"state management",
"deep links",
"deep linking",
"url synchronization",
"useUrlState",
"state in url",
"client components communication",
"query string",
"search params",
"query params",
"typescript",
"workflow",
"javascript",
"hooks",
"react.js",
"reactjs",
"react",
"NextJS",
"Next.js",
"nuqs alternative",
"query params parser",
"query params parsing"
],
"packageManager": "[email protected]"
}
6 changes: 2 additions & 4 deletions packages/urlstate/next/useUrlState/useUrlState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ export function useUrlState<T extends JSONCompatible>({
);

const updateUrl = React.useCallback(
(value?: Parameters<typeof updateUrlBase>[0], options?: Options) => {
const _opts = { ...defaultOptions, ...opts, ...options };
updateUrlBase(value, _opts);
},
(value?: Parameters<typeof updateUrlBase>[0], options?: Options) =>
updateUrlBase(value, { ...defaultOptions, ...opts, ...options }),
[updateUrlBase, opts],
);

Expand Down
9 changes: 2 additions & 7 deletions packages/urlstate/react-router/useUrlState/useUrlState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,8 @@ export function useUrlState<T extends JSONCompatible>({
);

const updateUrl = React.useCallback(
(
value?: Parameters<typeof updateUrlBase>[0],
options?: NavigateOptions,
) => {
const opts = { ...defaultOpts, ...initOpts, ...options };
updateUrlBase(value, opts);
},
(value?: Parameters<typeof updateUrlBase>[0], options?: NavigateOptions) =>
updateUrlBase(value, { ...defaultOpts, ...initOpts, ...options }),
[initOpts],
);

Expand Down
11 changes: 5 additions & 6 deletions packages/urlstate/useSharedState/useSharedState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ export function useSharedState<T extends JSONCompatible>(
const newVal = isFunc ? value(curr) : { ...curr, ...value };
if (isEqual(curr, newVal)) return void 0;
stateMap.set(stateShape.current, newVal);
subscribers.get(stateShape.current).forEach((sub) => {
sub();
});
subscribers.get(stateShape.current).forEach((sub) => sub());
},
[],
);
Expand All @@ -79,9 +77,10 @@ export function useSharedState<T extends JSONCompatible>(
}, []);

// get state without deps
const getState = React.useCallback(() => {
return stateMap.get(stateShape.current) || stateShape.current;
}, []);
const getState = React.useCallback(
() => stateMap.get(stateShape.current) || stateShape.current,
[],
);

return { state, getState, setState };
}
10 changes: 3 additions & 7 deletions packages/urlstate/useUrlEncode/useUrlEncode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ import { type JSONCompatible, typeOf } from "../utils";
*/
export function useUrlEncode<T extends JSONCompatible>(stateShape: T) {
const stringify = React.useCallback(
function (
state: typeof stateShape,
paramsToKeep?: string | URLSearchParams,
): string {
(state: typeof stateShape, paramsToKeep?: string | URLSearchParams) => {
return typeOf(state) === "object"
? encodeState(state, stateShape, paramsToKeep)
: "";
Expand All @@ -37,9 +34,8 @@ export function useUrlEncode<T extends JSONCompatible>(stateShape: T) {
);

const parse = React.useCallback(
function (strOrSearchParams: string | URLSearchParams) {
return decodeState(strOrSearchParams, stateShape) as typeof stateShape;
},
(strOrSearchParams: string | URLSearchParams) =>
decodeState(strOrSearchParams, stateShape) as typeof stateShape,
[stateShape],
);

Expand Down
12 changes: 6 additions & 6 deletions packages/urlstate/useUrlStateBase/useUrlStateBase.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ describe('useUrlStateBase', () => {
result.current.updateUrl({ ...shape, num: 50 });
});

await jest.runAllTimersAsync();
await new Promise(process.nextTick);

expect(result.current.state).toStrictEqual({ ...shape, num: 50 });
expect(router.push).toHaveBeenCalledTimes(1);
Expand All @@ -240,7 +240,7 @@ describe('useUrlStateBase', () => {
result.current.updateUrl({ num: 50 });
});

await jest.runAllTimersAsync();
await new Promise(process.nextTick);

expect(result.current.state).toStrictEqual({ ...shape, num: 50 });
expect(router.push).toHaveBeenCalledTimes(1);
Expand All @@ -258,7 +258,7 @@ describe('useUrlStateBase', () => {
result.current.updateUrl();
});

await jest.runAllTimersAsync();
await new Promise(process.nextTick);

expect(result.current.state).toStrictEqual({ ...shape, num: 50 });
expect(router.push).toHaveBeenCalledTimes(1);
Expand All @@ -274,7 +274,7 @@ describe('useUrlStateBase', () => {
result.current.updateUrl((curr) => ({ ...curr, num: 50 }));
});

await jest.runAllTimersAsync();
await new Promise(process.nextTick);

expect(result.current.state).toStrictEqual({ ...shape, num: 50 });
expect(router.push).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -309,7 +309,7 @@ describe('useUrlStateBase', () => {
result.current.updateUrl(newState);
});

await jest.runAllTimersAsync();
await new Promise(process.nextTick);

expect(router.push).toHaveBeenCalledTimes(1);
expect(router.push).toHaveBeenNthCalledWith(1, `/?num=55${hash}`, {});
Expand All @@ -328,7 +328,7 @@ describe('useUrlStateBase', () => {
});
});

await jest.runAllTimersAsync();
await new Promise(process.nextTick);

expect(result.current.state).toStrictEqual(newState);
expect(router.replace).toHaveBeenCalledTimes(1);
Expand Down
68 changes: 67 additions & 1 deletion packages/urlstate/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getParams, typeOf, assignValue } from './utils';
import { getParams, typeOf, assignValue, filterUnknownParamsClient } from './utils';

describe('typeOf', () => {
it('string', () => {
Expand Down Expand Up @@ -128,3 +128,69 @@ describe('assignValue', () => {
})

const clone = (obj: object) => JSON.parse(JSON.stringify(obj))

describe('filterUnknownParamsClient', () => {
afterAll(() => {
jest.resetAllMocks()
})

it('should include only the keys that exist in the shape', () => {
const originalLocation = window.location;
jest.spyOn(window, 'location', 'get').mockImplementation(() => ({
...originalLocation,
search: "?foo=bar&baz=qux",
}));
const result = filterUnknownParamsClient({ foo: '', dummy: '' });
expect(result).toBe("foo=bar");
});

it('should return an empty string if no keys match the shape', () => {
const originalLocation = window.location;
jest.spyOn(window, 'location', 'get').mockImplementation(() => ({
...originalLocation,
search: "?foo=bar&baz=qux",
}));
const result = filterUnknownParamsClient({ noKey: '' });
expect(result).toBe("");
});

it('should handle an empty URL search string returning an empty string', () => {
const originalLocation = window.location;
jest.spyOn(window, 'location', 'get').mockImplementation(() => ({
...originalLocation,
search: "",
}));
const result = filterUnknownParamsClient({ someKey: '' });
expect(result).toBe("");
});

it('should handle an empty shape returning an empty string', () => {
const originalLocation = window.location;
jest.spyOn(window, 'location', 'get').mockImplementation(() => ({
...originalLocation,
search: "?foo=bar&baz=qux",
}));
const result = filterUnknownParamsClient({});
expect(result).toBe("");
});

it('should handle special characters correctly', () => {
const originalLocation = window.location;
jest.spyOn(window, 'location', 'get').mockImplementation(() => ({
...originalLocation,
search: "?foo=bar%20baz&baz=qux",
}));
const result = filterUnknownParamsClient({ foo: '' });
expect(result).toBe("foo=bar+baz");
});

it('should manage repeated keys and take the last one', () => {
const originalLocation = window.location;
jest.spyOn(window, 'location', 'get').mockImplementation(() => ({
...originalLocation,
search: "?foo=first&foo=second",
}));
const result = filterUnknownParamsClient({ foo: '' });
expect(result).toBe("foo=second");
});
});
1 change: 0 additions & 1 deletion packages/urlstate/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export type UnknownObj = object | { [key: string]: unknown };
export const isEqual = (val1: unknown, val2: unknown) =>
JSON.stringify(val1) === JSON.stringify(val2);

// TODO: tests
export function filterUnknownParamsClient<T extends object>(shape: T) {
const shapeParams = new URLSearchParams();

Expand Down

0 comments on commit 7a10939

Please sign in to comment.