aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/hooks
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-11-24 20:00:08 +0100
committerArmand Philippot <git@armandphilippot.com>2023-11-27 14:47:51 +0100
commitf111685c5886f3e77edfd3621c98d8ac1b9bcce4 (patch)
tree62a541fe3afeb64bf745443706fbfb02e96c5230 /src/utils/hooks
parentbee515641cb144be9a855ff2cac258d2fedab21d (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.ts28
-rw-r--r--src/utils/hooks/use-comments.ts21
-rw-r--r--src/utils/hooks/use-pagination/use-pagination.test.ts9
-rw-r--r--src/utils/hooks/use-pagination/use-pagination.ts17
-rw-r--r--src/utils/hooks/use-posts-list/use-posts-list.test.ts4
-rw-r--r--src/utils/hooks/use-posts-list/use-posts-list.ts50
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,
};
};