diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-11-24 20:00:08 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-27 14:47:51 +0100 |
| commit | f111685c5886f3e77edfd3621c98d8ac1b9bcce4 (patch) | |
| tree | 62a541fe3afeb64bf745443706fbfb02e96c5230 /src/utils/hooks | |
| parent | bee515641cb144be9a855ff2cac258d2fedab21d (diff) | |
refactor(services, types): reorganize GraphQL fetchers and data types
The Typescript mapped types was useful for autocompletion in fetchers
but their are harder to maintain. I think it's better to keep each
query close to its fetcher to have a better understanding of the
fetched data. So I:
* colocate queries with their own fetcher
* colocate mutations with their own mutator
* remove Typescript mapped types for queries and mutations
* move data convertors inside graphql services
* rename most of data types and fetchers
Diffstat (limited to 'src/utils/hooks')
| -rw-r--r-- | src/utils/hooks/use-article.ts | 28 | ||||
| -rw-r--r-- | src/utils/hooks/use-comments.ts | 21 | ||||
| -rw-r--r-- | src/utils/hooks/use-pagination/use-pagination.test.ts | 9 | ||||
| -rw-r--r-- | src/utils/hooks/use-pagination/use-pagination.ts | 17 | ||||
| -rw-r--r-- | src/utils/hooks/use-posts-list/use-posts-list.test.ts | 4 | ||||
| -rw-r--r-- | src/utils/hooks/use-posts-list/use-posts-list.ts | 50 |
6 files changed, 69 insertions, 60 deletions
diff --git a/src/utils/hooks/use-article.ts b/src/utils/hooks/use-article.ts index f339f7f..5e54ee4 100644 --- a/src/utils/hooks/use-article.ts +++ b/src/utils/hooks/use-article.ts @@ -1,11 +1,7 @@ import { useEffect, useState } from 'react'; import useSWR from 'swr'; -import { - articleBySlugQuery, - fetchAPI, - getArticleFromRawData, -} from '../../services/graphql'; -import type { Article, Maybe, RawArticle } from '../../types'; +import { convertPostToArticle, fetchPost } from '../../services/graphql'; +import type { Article, Maybe } from '../../types'; export type UseArticleConfig = { /** @@ -28,24 +24,12 @@ export const useArticle = ({ slug, fallback, }: UseArticleConfig): Article | undefined => { - const { data } = useSWR( - slug ? { query: articleBySlugQuery, variables: { slug } } : null, - fetchAPI<RawArticle, typeof articleBySlugQuery>, - {} - ); - const [article, setArticle] = useState<Maybe<Article>>(); + const { data } = useSWR(slug, fetchPost, {}); + const [article, setArticle] = useState<Maybe<Article>>(fallback); useEffect(() => { - const getArticle = async () => { - if (data) { - setArticle(await getArticleFromRawData(data.post)); - } else { - setArticle(fallback); - } - }; - - getArticle(); - }, [data, fallback]); + if (data) convertPostToArticle(data).then((post) => setArticle(post)); + }, [data]); return article; }; diff --git a/src/utils/hooks/use-comments.ts b/src/utils/hooks/use-comments.ts index ac723e9..94a2d7e 100644 --- a/src/utils/hooks/use-comments.ts +++ b/src/utils/hooks/use-comments.ts @@ -1,9 +1,13 @@ import useSWR from 'swr'; -import { getAllComments } from '../../services/graphql'; +import { + type FetchCommentsListInput, + fetchCommentsList, + convertWPCommentToComment, + buildCommentsTree, +} from '../../services/graphql'; import type { SingleComment } from '../../types'; -export type UseCommentsConfig = { - contentId?: string | number; +export type UseCommentsConfig = FetchCommentsListInput & { fallback?: SingleComment[]; }; @@ -14,10 +18,15 @@ export type UseCommentsConfig = { * @returns {SingleComment[]|undefined} */ export const useComments = ({ - contentId, fallback, + ...input }: UseCommentsConfig): SingleComment[] | undefined => { - const { data } = useSWR(contentId ? { contentId } : null, getAllComments, {}); + const { data } = useSWR(input, fetchCommentsList, {}); - return data ?? fallback; + if (!data) return fallback; + + const comments = data.map(convertWPCommentToComment); + const commentsTree = buildCommentsTree(comments); + + return commentsTree; }; diff --git a/src/utils/hooks/use-pagination/use-pagination.test.ts b/src/utils/hooks/use-pagination/use-pagination.test.ts index 20cb37e..18f3ac5 100644 --- a/src/utils/hooks/use-pagination/use-pagination.test.ts +++ b/src/utils/hooks/use-pagination/use-pagination.test.ts @@ -1,8 +1,11 @@ import { beforeEach, describe, expect, it, jest } from '@jest/globals'; import { act, renderHook, waitFor } from '@testing-library/react'; import { getConnection } from '../../../../tests/utils/graphql'; -import type { EdgesResponse, GraphQLEdgesInput, Search } from '../../../types'; -import { usePagination } from './use-pagination'; +import type { GraphQLConnection } from '../../../types'; +import { + type UsePaginationFetcherInput, + usePagination, +} from './use-pagination'; type Data = { id: number; @@ -24,7 +27,7 @@ describe('usePagination', () => { after, first, search, - }: GraphQLEdgesInput & Search): Promise<EdgesResponse<Data>> => { + }: UsePaginationFetcherInput): Promise<GraphQLConnection<Data>> => { const filteredData = search ? data.filter((d) => d.title.includes(search)) : data; diff --git a/src/utils/hooks/use-pagination/use-pagination.ts b/src/utils/hooks/use-pagination/use-pagination.ts index 4df521b..2a40aa4 100644 --- a/src/utils/hooks/use-pagination/use-pagination.ts +++ b/src/utils/hooks/use-pagination/use-pagination.ts @@ -1,22 +1,25 @@ import { useCallback } from 'react'; import useSWRInfinite, { type SWRInfiniteKeyLoader } from 'swr/infinite'; import type { - EdgesResponse, + GraphQLConnection, GraphQLEdgesInput, Maybe, Nullable, - Search, } from '../../../types'; +export type UsePaginationFetcherInput = GraphQLEdgesInput & { + search?: string; +}; + export type UsePaginationConfig<T> = { /** * The initial data. */ - fallback?: EdgesResponse<T>[]; + fallback?: GraphQLConnection<T>[]; /** * A function to fetch more data. */ - fetcher: (props: GraphQLEdgesInput & Search) => Promise<EdgesResponse<T>>; + fetcher: (props: UsePaginationFetcherInput) => Promise<GraphQLConnection<T>>; /** * The number of results per page. */ @@ -31,7 +34,7 @@ export type UsePaginationReturn<T> = { /** * The data from the API. */ - data: Maybe<EdgesResponse<T>[]>; + data: Maybe<GraphQLConnection<T>[]>; /** * An error thrown by fetcher. */ @@ -88,8 +91,8 @@ export const usePagination = <T>({ perPage, searchQuery, }: UsePaginationConfig<T>): UsePaginationReturn<T> => { - const getKey: SWRInfiniteKeyLoader<EdgesResponse<T>> = useCallback( - (pageIndex, previousPageData): Nullable<GraphQLEdgesInput & Search> => { + const getKey: SWRInfiniteKeyLoader<GraphQLConnection<T>> = useCallback( + (pageIndex, previousPageData): Nullable<UsePaginationFetcherInput> => { if (previousPageData && !previousPageData.edges.length) return null; return { diff --git a/src/utils/hooks/use-posts-list/use-posts-list.test.ts b/src/utils/hooks/use-posts-list/use-posts-list.test.ts index 1d11111..ff69de2 100644 --- a/src/utils/hooks/use-posts-list/use-posts-list.test.ts +++ b/src/utils/hooks/use-posts-list/use-posts-list.test.ts @@ -1,13 +1,13 @@ import { describe, expect, it } from '@jest/globals'; import { act, renderHook } from '@testing-library/react'; -import { getArticles } from '../../../services/graphql'; +import { fetchPostsList } from '../../../services/graphql'; import { usePostsList } from './use-posts-list'; describe('usePostsList', () => { it('can return the first new result index when loading more posts', async () => { const perPage = 5; const { result } = renderHook(() => - usePostsList({ fetcher: getArticles, perPage }) + usePostsList({ fetcher: fetchPostsList, perPage }) ); expect.assertions(2); diff --git a/src/utils/hooks/use-posts-list/use-posts-list.ts b/src/utils/hooks/use-posts-list/use-posts-list.ts index 980d531..bb77f31 100644 --- a/src/utils/hooks/use-posts-list/use-posts-list.ts +++ b/src/utils/hooks/use-posts-list/use-posts-list.ts @@ -1,29 +1,34 @@ -import { useCallback, useEffect, useState } from 'react'; -import type { PostData } from '../../../components'; -import type { Maybe, RawArticle } from '../../../types'; -import { getPostsList } from '../../helpers'; +import { useCallback, useState } from 'react'; +import type { + ArticlePreview, + GraphQLConnection, + GraphQLEdge, + Maybe, + WPPostPreview, +} from '../../../types'; import { type UsePaginationConfig, usePagination, type UsePaginationReturn, } from '../use-pagination'; +import { convertPostPreviewToArticlePreview } from 'src/services/graphql'; export type usePostsListReturn = Omit< - UsePaginationReturn<RawArticle>, + UsePaginationReturn<WPPostPreview>, 'data' > & { /** - * The index of the first new result when loading more posts. + * The articles list. */ - firstNewResultIndex: Maybe<number>; + articles: Maybe<GraphQLConnection<ArticlePreview>[]>; /** - * The posts list. + * The index of the first new result when loading more posts. */ - posts: Maybe<PostData[]>; + firstNewResultIndex: Maybe<number>; }; export const usePostsList = ( - config: UsePaginationConfig<RawArticle> + config: UsePaginationConfig<WPPostPreview> ): usePostsListReturn => { const { data, @@ -40,15 +45,6 @@ export const usePostsList = ( } = usePagination(config); const [firstNewResultIndex, setFirstNewResultIndex] = useState<Maybe<number>>(undefined); - const [posts, setPosts] = useState<Maybe<PostData[]>>(undefined); - - useEffect(() => { - const getPosts = async () => { - if (data) setPosts(await getPostsList(data)); - }; - - getPosts(); - }, [data]); const handleLoadMore = useCallback(async () => { setFirstNewResultIndex(size * config.perPage + 1); @@ -56,7 +52,22 @@ export const usePostsList = ( await loadMore(); }, [config.perPage, loadMore, size]); + const articles: Maybe<GraphQLConnection<ArticlePreview>[]> = data?.map( + (page): GraphQLConnection<ArticlePreview> => { + return { + edges: page.edges.map((edge): GraphQLEdge<ArticlePreview> => { + return { + cursor: edge.cursor, + node: convertPostPreviewToArticlePreview(edge.node), + }; + }), + pageInfo: page.pageInfo, + }; + } + ); + return { + articles, error, firstNewResultIndex, hasNextPage, @@ -67,7 +78,6 @@ export const usePostsList = ( isRefreshing, isValidating, loadMore: handleLoadMore, - posts, size, }; }; |
