From 5f4e4c11c6db779015cf22f696d06348645c6ec0 Mon Sep 17 00:00:00 2001 From: Filip Lindahl Date: Mon, 26 Feb 2024 20:14:52 +0100 Subject: [PATCH 1/4] setup test for initial value --- __tests__/atomWithHash_spec.tsx | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/__tests__/atomWithHash_spec.tsx b/__tests__/atomWithHash_spec.tsx index 443b5b4..1491fa9 100644 --- a/__tests__/atomWithHash_spec.tsx +++ b/__tests__/atomWithHash_spec.tsx @@ -1,4 +1,4 @@ -import React, { StrictMode, useMemo } from 'react'; +import React, { StrictMode, useEffect, useMemo, useState } from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { useAtom } from 'jotai/react'; @@ -186,6 +186,37 @@ describe('atomWithHash', () => { await user.type(screen.getByLabelText('b'), '1'); await waitFor(() => expect(paramBMockFn).toBeCalledTimes(4)); }); + + it('sets initial value from hash', async () => { + window.location.hash = '#count=2'; + const countAtom = atomWithHash('count', 0); + + const Counter = () => { + const [count] = useAtom(countAtom); + const [countWasZero, setCountWasZero] = useState(false); + + useEffect(() => { + if (count === 0) { + setCountWasZero(true); + } + }, [count]); + return ( + <> +
count: {count}
+
count was zero: {countWasZero.toString()}
+ + ); + }; + + const { findByText } = render( + + + , + ); + + await findByText('count: 2'); + await findByText('count was zero: false'); + }); }); describe('atomWithHash without window', () => { From 1491143648906276cc1e07935dda3a3cc9634886 Mon Sep 17 00:00:00 2001 From: Filip Lindahl Date: Mon, 26 Feb 2024 21:01:22 +0100 Subject: [PATCH 2/4] use value in hash if present --- src/atomWithHash.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/atomWithHash.ts b/src/atomWithHash.ts index 120b6e9..7491833 100644 --- a/src/atomWithHash.ts +++ b/src/atomWithHash.ts @@ -26,6 +26,12 @@ export function atomWithHash( }, ): WritableAtom], void> { const serialize = options?.serialize || JSON.stringify; + let hashValueInURL; + if (global.window) { + hashValueInURL = new URLSearchParams(window.location.hash.slice(1)).get( + key, + ); + } const deserialize = options?.deserialize || safeJSONParse(initialValue); const subscribe = options?.subscribe || @@ -51,7 +57,7 @@ export function atomWithHash( if (typeof setHashOption === 'function') { setHash = setHashOption; } - const strAtom = atom(null); + const strAtom = atom(hashValueInURL ?? null); strAtom.onMount = (setAtom) => { if (typeof window === 'undefined' || !window.location) { return undefined; From 80c21f93cc1e54325b63f0ebd98082ae1f1e7510 Mon Sep 17 00:00:00 2001 From: Filip Lindahl Date: Tue, 27 Feb 2024 08:22:35 +0100 Subject: [PATCH 3/4] adjust after comments on PR --- src/atomWithHash.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/atomWithHash.ts b/src/atomWithHash.ts index 7491833..caf0c16 100644 --- a/src/atomWithHash.ts +++ b/src/atomWithHash.ts @@ -26,9 +26,9 @@ export function atomWithHash( }, ): WritableAtom], void> { const serialize = options?.serialize || JSON.stringify; - let hashValueInURL; - if (global.window) { - hashValueInURL = new URLSearchParams(window.location.hash.slice(1)).get( + let initialValueInHash: string | null = null; + if (typeof window !== 'undefined') { + initialValueInHash = new URLSearchParams(window.location.hash.slice(1)).get( key, ); } @@ -57,7 +57,7 @@ export function atomWithHash( if (typeof setHashOption === 'function') { setHash = setHashOption; } - const strAtom = atom(hashValueInURL ?? null); + const strAtom = atom(initialValueInHash); strAtom.onMount = (setAtom) => { if (typeof window === 'undefined' || !window.location) { return undefined; From ec84c87d2d0d40295fba2dc8fe69652ff00cd8d7 Mon Sep 17 00:00:00 2001 From: Filip Lindahl Date: Tue, 27 Feb 2024 08:52:23 +0100 Subject: [PATCH 4/4] refactor more, cleaner more contained solution --- src/atomWithHash.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/atomWithHash.ts b/src/atomWithHash.ts index caf0c16..76b5d58 100644 --- a/src/atomWithHash.ts +++ b/src/atomWithHash.ts @@ -26,12 +26,7 @@ export function atomWithHash( }, ): WritableAtom], void> { const serialize = options?.serialize || JSON.stringify; - let initialValueInHash: string | null = null; - if (typeof window !== 'undefined') { - initialValueInHash = new URLSearchParams(window.location.hash.slice(1)).get( - key, - ); - } + const deserialize = options?.deserialize || safeJSONParse(initialValue); const subscribe = options?.subscribe || @@ -57,15 +52,20 @@ export function atomWithHash( if (typeof setHashOption === 'function') { setHash = setHashOption; } - const strAtom = atom(initialValueInHash); + const isLocationAvailable = + typeof window !== 'undefined' && !!window.location; + + const strAtom = atom( + isLocationAvailable + ? new URLSearchParams(window.location.hash.slice(1)).get(key) + : null, + ); strAtom.onMount = (setAtom) => { - if (typeof window === 'undefined' || !window.location) { + if (!isLocationAvailable) { return undefined; } const callback = () => { - const searchParams = new URLSearchParams(window.location.hash.slice(1)); - const str = searchParams.get(key); - setAtom(str); + setAtom(new URLSearchParams(window.location.hash.slice(1)).get(key)); }; const unsubscribe = subscribe(callback); callback();