diff --git a/.changeset/pretty-panthers-deliver.md b/.changeset/pretty-panthers-deliver.md new file mode 100644 index 00000000000..dd470a77b34 --- /dev/null +++ b/.changeset/pretty-panthers-deliver.md @@ -0,0 +1,5 @@ +--- +'@graphiql/react': patch +--- + +create instance of `new HistoryStore` and `new StorageAPI` only on mount, use function with `useState` diff --git a/packages/graphiql-react/src/history/context.tsx b/packages/graphiql-react/src/history/context.tsx index 1a51e4a8487..6d3068595e8 100644 --- a/packages/graphiql-react/src/history/context.tsx +++ b/packages/graphiql-react/src/history/context.tsx @@ -1,5 +1,5 @@ import { HistoryStore, QueryStoreItem, StorageAPI } from '@graphiql/toolkit'; -import { ReactNode, useCallback, useMemo, useRef, useState } from 'react'; +import { ReactNode, useMemo, useState } from 'react'; import { useStorageContext } from '../storage'; import { createContextHook, createNullableContext } from '../utility/context'; @@ -87,77 +87,44 @@ export type HistoryContextProviderProps = { * any additional props they added for their needs (i.e., build their own functions that may save * to a backend instead of localStorage and might need an id property added to the QueryStoreItem) */ -export function HistoryContextProvider(props: HistoryContextProviderProps) { +export function HistoryContextProvider({ + maxHistoryLength = DEFAULT_HISTORY_LENGTH, + children, +}: HistoryContextProviderProps) { const storage = useStorageContext(); - const historyStore = useRef( - new HistoryStore( + const [historyStore] = useState( + () => // Fall back to a noop storage when the StorageContext is empty - storage || new StorageAPI(null), - props.maxHistoryLength || DEFAULT_HISTORY_LENGTH, - ), + new HistoryStore(storage || new StorageAPI(null), maxHistoryLength), ); - const [items, setItems] = useState(historyStore.current?.queries || []); - - const addToHistory: HistoryContextType['addToHistory'] = useCallback( - (operation: QueryStoreItem) => { - historyStore.current?.updateHistory(operation); - setItems(historyStore.current.queries); - }, - [], - ); - - const editLabel: HistoryContextType['editLabel'] = useCallback( - (operation: QueryStoreItem, index?: number) => { - historyStore.current.editLabel(operation, index); - setItems(historyStore.current.queries); - }, - [], - ); - - const toggleFavorite: HistoryContextType['toggleFavorite'] = useCallback( - (operation: QueryStoreItem) => { - historyStore.current.toggleFavorite(operation); - setItems(historyStore.current.queries); - }, - [], - ); - - const setActive: HistoryContextType['setActive'] = useCallback( - (item: QueryStoreItem) => { - return item; - }, - [], - ); - - const deleteFromHistory: HistoryContextType['deleteFromHistory'] = - useCallback((item: QueryStoreItem, clearFavorites = false) => { - historyStore.current.deleteHistory(item, clearFavorites); - setItems(historyStore.current.queries); - }, []); + const [items, setItems] = useState(() => historyStore.queries || []); const value = useMemo( () => ({ - addToHistory, - editLabel, + addToHistory(operation) { + historyStore.updateHistory(operation); + setItems(historyStore.queries); + }, + editLabel(operation, index) { + historyStore.editLabel(operation, index); + setItems(historyStore.queries); + }, items, - toggleFavorite, - setActive, - deleteFromHistory, + toggleFavorite(operation) { + historyStore.toggleFavorite(operation); + setItems(historyStore.queries); + }, + setActive: item => item, + deleteFromHistory(item, clearFavorites) { + historyStore.deleteHistory(item, clearFavorites); + setItems(historyStore.queries); + }, }), - [ - addToHistory, - editLabel, - items, - toggleFavorite, - setActive, - deleteFromHistory, - ], + [items, historyStore], ); return ( - - {props.children} - + {children} ); } diff --git a/packages/graphiql-react/src/storage.tsx b/packages/graphiql-react/src/storage.tsx index a8d9f0740f2..a08c7c990e1 100644 --- a/packages/graphiql-react/src/storage.tsx +++ b/packages/graphiql-react/src/storage.tsx @@ -12,7 +12,7 @@ export type StorageContextProviderProps = { children: ReactNode; /** * Provide a custom storage API. - * @default `localStorage`` + * @default `localStorage` * @see {@link https://graphiql-test.netlify.app/typedoc/modules/graphiql_toolkit.html#storage-2|API docs} * for details on the required interface. */ @@ -21,7 +21,7 @@ export type StorageContextProviderProps = { export function StorageContextProvider(props: StorageContextProviderProps) { const isInitialRender = useRef(true); - const [storage, setStorage] = useState(new StorageAPI(props.storage)); + const [storage, setStorage] = useState(() => new StorageAPI(props.storage)); useEffect(() => { if (isInitialRender.current) {