diff --git a/.changeset/dull-walls-smoke.md b/.changeset/dull-walls-smoke.md new file mode 100644 index 000000000..06f2a8d08 --- /dev/null +++ b/.changeset/dull-walls-smoke.md @@ -0,0 +1,7 @@ +--- +"@lens-protocol/react": minor +"@lens-protocol/react-native": minor +"@lens-protocol/react-web": minor +--- + +**feat:** adds React Suspense support to `useSearchProfiles` hook diff --git a/examples/web/src/discovery/UseSearchProfiles.tsx b/examples/web/src/discovery/UseSearchProfiles.tsx index 758d8bdfc..3f1fc38d3 100644 --- a/examples/web/src/discovery/UseSearchProfiles.tsx +++ b/examples/web/src/discovery/UseSearchProfiles.tsx @@ -1,7 +1,6 @@ import { useSearchProfiles } from '@lens-protocol/react-web'; -import { ChangeEvent, useState } from 'react'; +import { ChangeEvent, Suspense, startTransition, useState } from 'react'; -import { ErrorMessage } from '../components/error/ErrorMessage'; import { Loading } from '../components/loading/Loading'; import { useInfiniteScroll } from '../hooks/useInfiniteScroll'; import { ProfileCard } from '../profiles/components/ProfileCard'; @@ -11,17 +10,14 @@ type SearchResultsProps = { }; function SearchResults({ query }: SearchResultsProps) { - const { data, error, loading, hasMore, observeRef } = useInfiniteScroll( - useSearchProfiles({ query }), + const { data, hasMore, observeRef } = useInfiniteScroll( + useSearchProfiles({ query, suspense: true }), ); - if (loading) return ; - - if (error) return ; - if (data.length === 0) { return

No profiles found

; } + return (
{data.map((profile) => ( @@ -37,7 +33,9 @@ export function UseSearchProfiles() { const [selectedQuery, setSelectedQuery] = useState(); const handleSubmit = () => { - setSelectedQuery(inputValue); + startTransition(() => { + setSelectedQuery(inputValue); + }); }; const handleChange = (e: ChangeEvent) => { @@ -53,7 +51,10 @@ export function UseSearchProfiles() {
- {selectedQuery && } + + }> + {selectedQuery && } + ); } diff --git a/packages/react/src/discovery/useSearchProfiles.ts b/packages/react/src/discovery/useSearchProfiles.ts index ed720bd66..580c648d4 100644 --- a/packages/react/src/discovery/useSearchProfiles.ts +++ b/packages/react/src/discovery/useSearchProfiles.ts @@ -1,22 +1,37 @@ import { Profile, ProfileSearchRequest, - useSearchProfiles as useBaseSearchProfiles, + ProfileSearchWhere, + SearchProfilesDocument, } from '@lens-protocol/api-bindings'; import { useLensApolloClient } from '../helpers/arguments'; -import { PaginatedArgs, PaginatedReadResult, usePaginatedReadResult } from '../helpers/reads'; +import { PaginatedArgs, PaginatedReadResult } from '../helpers/reads'; +import { + SuspendablePaginatedResult, + SuspenseEnabled, + SuspensePaginatedResult, + useSuspendablePaginatedQuery, +} from '../helpers/suspense'; import { useFragmentVariables } from '../helpers/variables'; +/** + * {@link useSearchProfiles} hook arguments + */ export type UseSearchProfilesArgs = PaginatedArgs; +export type { ProfileSearchRequest, ProfileSearchWhere }; + /** - * `useSearchProfiles` is a paginated hook that lets you search for profiles based on a defined criteria + * {@link useSearchProfiles} hook arguments with Suspense support * - * @category Discovery - * @group Hooks + * @experimental This API can change without notice + */ +export type UseSuspenseSearchProfilesArgs = SuspenseEnabled; + +/** + * `useSearchProfiles` is a paginated hook that lets you search for profiles based on a defined criteria * - * @example * ```tsx * function SearchProfiles() { * const { data, error, loading } = useSearchProfiles({ query: 'foo' }); @@ -34,13 +49,58 @@ export type UseSearchProfilesArgs = PaginatedArgs; * ); * } * ``` + * + * @category Discovery + * @group Hooks */ -export function useSearchProfiles(args: UseSearchProfilesArgs): PaginatedReadResult { - return usePaginatedReadResult( - useBaseSearchProfiles( - useLensApolloClient({ - variables: useFragmentVariables(args), - }), - ), - ); +export function useSearchProfiles(args: UseSearchProfilesArgs): PaginatedReadResult; + +/** + * `useSearchProfiles` is a paginated hook that lets you search for profiles based on a defined criteria + * + * This signature supports [React Suspense](https://react.dev/reference/react/Suspense). + * + * ```tsx + * const { data } = useSearchProfiles({ + * query: 'foo', + * suspense: true, + * }); + * + * console.log(data); + * ``` + * + * Use [startTransition](https://react.dev/reference/react/startTransition) to avoid to re-suspend the component. + * + * ```tsx + * const [query, setQuery] = useState('bob'); + * + * const { data } = useSearchProfiles({ + * query, + * suspense: true, + * }); + * + * const search = startTransition(() => { + * setQuery('foo'); + * }); + * ``` + * + * @experimental This API can change without notice + * @category Discovery + * @group Hooks + */ +export function useSearchProfiles( + args: UseSuspenseSearchProfilesArgs, +): SuspensePaginatedResult; + +export function useSearchProfiles({ + suspense = false, + ...args +}: UseSearchProfilesArgs & { suspense?: boolean }): SuspendablePaginatedResult { + return useSuspendablePaginatedQuery({ + suspense, + query: SearchProfilesDocument, + options: useLensApolloClient({ + variables: useFragmentVariables(args), + }), + }); }