Docs and format: Update documentation for hooks and format code

Docs: begin work on updating documentation for hooks
This commit is contained in:
Aaron William Po
2023-05-03 00:07:24 -04:00
parent 196d09161a
commit 1971959ea4
16 changed files with 144 additions and 60 deletions

View File

@@ -3,9 +3,12 @@ import { useRouter } from 'next/router';
import { useContext } from 'react'; import { useContext } from 'react';
/** /**
* Custom React hook that redirects the user to the home page if they are logged in. This * Custom React hook that redirects the user to the home page if they are logged in.
* hook is used to prevent logged in users from accessing the login and signup pages. Must *
* be used under the UserContext provider. * This hook is used to prevent logged in users from accessing the login and signup pages
* by redirecting them to the home page.
*
* This hook should only be used in a component that is under the UserContext provider.
* *
* @returns {void} * @returns {void}
*/ */

View File

@@ -5,11 +5,13 @@ import useSWR from 'swr';
/** /**
* A custom React hook that fetches the current user's data from the server. * A custom React hook that fetches the current user's data from the server.
* *
* @returns An object containing the current user's data, a boolean indicating if the * @returns An object with the following properties:
* request is currently loading, and an error object if an error occurred during the *
* request. * - `user`: The current user's data.
* @throws When the user is not logged in, the server returns an error status code, or if * - `isLoading`: A boolean indicating whether the request is still in progress.
* the response data fails to validate against the expected schema. * - `error`: An error object if the user is not logged in, if the response data fails to
* validate against the expected schema, or if the server returns an error.
* - `mutate`: A function that can be used to mutate the current user's data.
*/ */
const useUser = () => { const useUser = () => {
const { const {

View File

@@ -16,22 +16,31 @@ interface UseBeerPostCommentsProps {
* @param props.pageNum - The page number of the comments to fetch. * @param props.pageNum - The page number of the comments to fetch.
* @param props.id - The ID of the beer post to fetch comments for. * @param props.id - The ID of the beer post to fetch comments for.
* @param props.pageSize - The number of comments to fetch per page. * @param props.pageSize - The number of comments to fetch per page.
* @returns An object containing the fetched comments, the total number of comment pages, * @returns An object with the following properties:
* a boolean indicating if the request is currently loading, and a function to mutate *
* the data. * - `comments`: The comments for the beer post.
* - `isLoading`: A boolean indicating whether the comments are being fetched.
* - `error`: The error that occurred while fetching the comments.
* - `mutate`: A function to mutate the comments.
* - `size`: The number of pages of comments that have been fetched.
* - `setSize`: A function to set the number of pages of comments that have been fetched.
* - `isLoadingMore`: A boolean indicating whether more comments are being fetched.
* - `isAtEnd`: A boolean indicating whether all comments have been fetched.
* - `pageCount`: The total number of pages of comments.
*/ */
const useBeerPostComments = ({ id, pageSize }: UseBeerPostCommentsProps) => { const useBeerPostComments = ({ id, pageSize }: UseBeerPostCommentsProps) => {
const fetcher = async (url: string) => { const fetcher = async (url: string) => {
const response = await fetch(url); const response = await fetch(url);
const json = await response.json(); const json = await response.json();
const count = response.headers.get('X-Total-Count'); const count = response.headers.get('X-Total-Count');
const parsed = APIResponseValidationSchema.safeParse(json);
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) { if (!parsed.success) {
throw new Error(parsed.error.message); throw new Error(parsed.error.message);
} }
const parsedPayload = z.array(CommentQueryResult).safeParse(parsed.data.payload);
const parsedPayload = z.array(CommentQueryResult).safeParse(parsed.data.payload);
if (!parsedPayload.success) { if (!parsedPayload.success) {
throw new Error(parsedPayload.error.message); throw new Error(parsedPayload.error.message);
} }

View File

@@ -6,8 +6,12 @@ import useSWR from 'swr';
* Custom hook to fetch the like count for a beer post from the server. * Custom hook to fetch the like count for a beer post from the server.
* *
* @param beerPostId - The ID of the beer post to fetch the like count for. * @param beerPostId - The ID of the beer post to fetch the like count for.
* @returns An object with the current like count, as well as metadata about the current * @returns An object with the following properties:
* state of the request. *
* - `error`: The error that occurred while fetching the like count.
* - `isLoading`: A boolean indicating whether the like count is being fetched.
* - `mutate`: A function to mutate the like count.
* - `likeCount`: The like count for the beer post.
*/ */
const useGetBeerPostLikeCount = (beerPostId: string) => { const useGetBeerPostLikeCount = (beerPostId: string) => {

View File

@@ -9,22 +9,23 @@ import { z } from 'zod';
* data from the server. * data from the server.
* *
* @param beerPostId The ID of the beer post to check for likes. * @param beerPostId The ID of the beer post to check for likes.
* @returns An object containing a boolean indicating if the user has liked the beer post, * @returns An object with the following properties:
* an error object if an error occurred during the request, and a boolean indicating if *
* the request is currently loading. * - `error`: The error that occurred while fetching the data.
* @throws When the user is not logged in, the server returns an error status code, or if * - `isLoading`: A boolean indicating whether the data is being fetched.
* the response data fails to validate against the expected schema. * - `mutate`: A function to mutate the data.
* - `isLiked`: A boolean indicating whether the current user has liked the beer post.
*/ */
const useCheckIfUserLikesBeerPost = (beerPostId: string) => { const useCheckIfUserLikesBeerPost = (beerPostId: string) => {
const { user } = useContext(UserContext); const { user } = useContext(UserContext);
const { data, error, isLoading, mutate } = useSWR( const { data, error, isLoading, mutate } = useSWR(
`/api/beers/${beerPostId}/like/is-liked`, `/api/beers/${beerPostId}/like/is-liked`,
async () => { async (url) => {
if (!user) { if (!user) {
throw new Error('User is not logged in.'); throw new Error('User is not logged in.');
} }
const response = await fetch(`/api/beers/${beerPostId}/like/is-liked`); const response = await fetch(url);
const json = await response.json(); const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json); const parsed = APIResponseValidationSchema.safeParse(json);

View File

@@ -5,9 +5,11 @@ import { z } from 'zod';
* A custom React hook that searches for beer posts that match a given query string. * A custom React hook that searches for beer posts that match a given query string.
* *
* @param query The search query string to match beer posts against. * @param query The search query string to match beer posts against.
* @returns An object containing an array of search results matching the query, an error * @returns An object with the following properties:
* object if an error occurred during the search, and a boolean indicating if the *
* request is currently loading. * - `searchResults`: The beer posts that match the search query.
* - `searchError`: The error that occurred while fetching the data.
* - `isLoading`: A boolean indicating whether the data is being fetched.
*/ */
const useBeerPostSearch = (query: string | undefined) => { const useBeerPostSearch = (query: string | undefined) => {
const { data, isLoading, error } = useSWR( const { data, isLoading, error } = useSWR(

View File

@@ -8,7 +8,16 @@ import { z } from 'zod';
* *
* @param options The options to use when fetching beer posts. * @param options The options to use when fetching beer posts.
* @param options.pageSize The number of beer posts to fetch per page. * @param options.pageSize The number of beer posts to fetch per page.
* @returns An object containing the beer posts, page count, and loading state. * @returns An object with the following properties:
*
* - `beerPosts`: The beer posts fetched from the API.
* - `error`: The error that occurred while fetching the data.
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
* - `isLoading`: A boolean indicating whether the data is being fetched.
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
* - `pageCount`: The total number of pages of data.
* - `setSize`: A function to set the size of the data.
* - `size`: The size of the data.
*/ */
const useBeerPosts = ({ pageSize }: { pageSize: number }) => { const useBeerPosts = ({ pageSize }: { pageSize: number }) => {
const fetcher = async (url: string) => { const fetcher = async (url: string) => {
@@ -49,13 +58,13 @@ const useBeerPosts = ({ pageSize }: { pageSize: number }) => {
return { return {
beerPosts, beerPosts,
pageCount, error: error as unknown,
size, isAtEnd,
setSize,
isLoading, isLoading,
isLoadingMore, isLoadingMore,
isAtEnd, pageCount,
error: error as unknown, setSize,
size,
}; };
}; };

View File

@@ -14,7 +14,16 @@ interface UseBeerPostsByBreweryParams {
* @param options The options to use when fetching beer posts. * @param options The options to use when fetching beer posts.
* @param options.pageSize The number of beer posts to fetch per page. * @param options.pageSize The number of beer posts to fetch per page.
* @param options.breweryId The ID of the brewery to fetch beer posts for. * @param options.breweryId The ID of the brewery to fetch beer posts for.
* @returns An object containing the beer posts, page count, and loading state. * @returns An object with the following properties:
*
* - `beerPosts`: The beer posts fetched from the API.
* - `error`: The error that occurred while fetching the data.
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
* - `isLoading`: A boolean indicating whether the data is being fetched.
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
* - `pageCount`: The total number of pages of data.
* - `setSize`: A function to set the size of the data.
* - `size`: The size of the data.
*/ */
const UseBeerPostsByBrewery = ({ pageSize, breweryId }: UseBeerPostsByBreweryParams) => { const UseBeerPostsByBrewery = ({ pageSize, breweryId }: UseBeerPostsByBreweryParams) => {
const fetcher = async (url: string) => { const fetcher = async (url: string) => {

View File

@@ -15,28 +15,35 @@ interface UseBreweryPostCommentsProps {
* @param props.pageNum - The page number of the comments to fetch. * @param props.pageNum - The page number of the comments to fetch.
* @param props.id - The ID of the brewery post to fetch comments for. * @param props.id - The ID of the brewery post to fetch comments for.
* @param props.pageSize - The number of comments to fetch per page. * @param props.pageSize - The number of comments to fetch per page.
* @returns An object containing the fetched comments, the total number of comment pages, * @returns An object with the following properties:
* a boolean indicating if the request is currently loading, and a function to mutate *
* the data. * - `comments`: The comments fetched from the API.
* - `error`: The error that occurred while fetching the data.
* - `isLoading`: A boolean indicating whether the data is being fetched.
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
* - `mutate`: A function to mutate the data.
* - `pageCount`: The total number of pages of data.
* - `setSize`: A function to set the size of the data.
* - `size`: The size of the data.
*/ */
const useBreweryPostComments = ({ id, pageSize }: UseBreweryPostCommentsProps) => { const useBreweryPostComments = ({ id, pageSize }: UseBreweryPostCommentsProps) => {
const fetcher = async (url: string) => { const fetcher = async (url: string) => {
const response = await fetch(url); const response = await fetch(url);
const json = await response.json(); const json = await response.json();
const count = response.headers.get('X-Total-Count'); const count = response.headers.get('X-Total-Count');
const parsed = APIResponseValidationSchema.safeParse(json);
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) { if (!parsed.success) {
throw new Error(parsed.error.message); throw new Error(parsed.error.message);
} }
const parsedPayload = z.array(CommentQueryResult).safeParse(parsed.data.payload);
const parsedPayload = z.array(CommentQueryResult).safeParse(parsed.data.payload);
if (!parsedPayload.success) { if (!parsedPayload.success) {
throw new Error(parsedPayload.error.message); throw new Error(parsedPayload.error.message);
} }
const pageCount = Math.ceil(parseInt(count as string, 10) / pageSize); const pageCount = Math.ceil(parseInt(count as string, 10) / pageSize);
return { comments: parsedPayload.data, pageCount }; return { comments: parsedPayload.data, pageCount };
}; };

View File

@@ -4,6 +4,18 @@ import { useContext } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { z } from 'zod'; import { z } from 'zod';
/**
* A custom React hook that checks if the current user likes a given brewery post.
*
* @param breweryPostId - The ID of the brewery post to check.
* @returns An object with the following properties:
*
* - `isLiked`: A boolean indicating whether the current user likes the brewery post.
* - `error`: The error that occurred while fetching the data.
* - `isLoading`: A boolean indicating whether the data is being fetched.
* - `mutate`: A function to mutate the data.
*/
const useCheckIfUserLikesBreweryPost = (breweryPostId: string) => { const useCheckIfUserLikesBreweryPost = (breweryPostId: string) => {
const { user } = useContext(UserContext); const { user } = useContext(UserContext);
const { data, error, isLoading, mutate } = useSWR( const { data, error, isLoading, mutate } = useSWR(
@@ -15,8 +27,8 @@ const useCheckIfUserLikesBreweryPost = (breweryPostId: string) => {
const response = await fetch(`/api/breweries/${breweryPostId}/like/is-liked`); const response = await fetch(`/api/breweries/${breweryPostId}/like/is-liked`);
const json = await response.json(); const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json); const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) { if (!parsed.success) {
throw new Error('Invalid API response.'); throw new Error('Invalid API response.');
} }

View File

@@ -2,6 +2,17 @@ import APIResponseValidationSchema from '@/validation/APIResponseValidationSchem
import useSWR from 'swr'; import useSWR from 'swr';
import { z } from 'zod'; import { z } from 'zod';
/**
* A custom React hook that fetches the number of likes a brewery post has.
*
* @param breweryPostId
* @returns An object with the following properties:
*
* - `likeCount`: The number of likes the brewery post has.
* - `error`: The error that occurred while fetching the data.
* - `isLoading`: A boolean indicating whether the data is being fetched.
* - `mutate`: A function to mutate the data.
*/
const useGetBreweryPostLikeCount = (breweryPostId: string) => { const useGetBreweryPostLikeCount = (breweryPostId: string) => {
const { error, mutate, data, isLoading } = useSWR( const { error, mutate, data, isLoading } = useSWR(
`/api/breweries/${breweryPostId}/like`, `/api/breweries/${breweryPostId}/like`,
@@ -10,15 +21,12 @@ const useGetBreweryPostLikeCount = (breweryPostId: string) => {
const json = await response.json(); const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json); const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) { if (!parsed.success) {
throw new Error('Failed to parse API response'); throw new Error('Failed to parse API response');
} }
const parsedPayload = z const parsedPayload = z
.object({ .object({ likeCount: z.number() })
likeCount: z.number(),
})
.safeParse(parsed.data.payload); .safeParse(parsed.data.payload);
if (!parsedPayload.success) { if (!parsedPayload.success) {

View File

@@ -8,7 +8,16 @@ import { z } from 'zod';
* *
* @param options The options to use when fetching brewery posts. * @param options The options to use when fetching brewery posts.
* @param options.pageSize The number of brewery posts to fetch per page. * @param options.pageSize The number of brewery posts to fetch per page.
* @returns An object containing the brewery posts, page count, and loading state. * @returns An object with the following properties:
*
* - `breweryPosts`: The brewery posts fetched from the API.
* - `error`: The error that occurred while fetching the data.
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
* - `isLoading`: A boolean indicating whether the data is being fetched.
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
* - `pageCount`: The total number of pages of data.
* - `setSize`: A function to set the size of the data.
* - `size`: The size of the data.
*/ */
const useBreweryPosts = ({ pageSize }: { pageSize: number }) => { const useBreweryPosts = ({ pageSize }: { pageSize: number }) => {
const fetcher = async (url: string) => { const fetcher = async (url: string) => {
@@ -31,10 +40,7 @@ const useBreweryPosts = ({ pageSize }: { pageSize: number }) => {
} }
const pageCount = Math.ceil(parseInt(count as string, 10) / pageSize); const pageCount = Math.ceil(parseInt(count as string, 10) / pageSize);
return { return { breweryPosts: parsedPayload.data, pageCount };
breweryPosts: parsedPayload.data,
pageCount,
};
}; };
const { data, error, isLoading, setSize, size } = useSWRInfinite( const { data, error, isLoading, setSize, size } = useSWRInfinite(

View File

@@ -4,8 +4,7 @@ import { useEffect, useState } from 'react';
* A custom React Hook that retrieves and monitors the user's geolocation using the * A custom React Hook that retrieves and monitors the user's geolocation using the
* browser's built-in `navigator.geolocation` API. * browser's built-in `navigator.geolocation` API.
* *
* @returns An object containing the user's geolocation information and any errors that * @returns An object with the following properties:
* might occur. The object has the following properties:
* *
* - `coords` - The user's current geolocation coordinates, or null if the geolocation could * - `coords` - The user's current geolocation coordinates, or null if the geolocation could
* not be retrieved. * not be retrieved.

View File

@@ -11,7 +11,10 @@ interface Page {
* A custom hook that returns the current URL and the pages to display in the navbar. It * A custom hook that returns the current URL and the pages to display in the navbar. It
* uses the user context to determine whether the user is authenticated or not. * uses the user context to determine whether the user is authenticated or not.
* *
* @returns An object containing the current URL and the pages to display in the navbar. * @returns An object with the following properties:
*
* - `currentURL`: The current URL.
* - `pages`: The pages to display in the navbar.
*/ */
const useNavbar = () => { const useNavbar = () => {
const router = useRouter(); const router = useRouter();

View File

@@ -2,13 +2,18 @@ import { useState, useEffect } from 'react';
import useMediaQuery from './useMediaQuery'; import useMediaQuery from './useMediaQuery';
/** /**
* A custom hook to manage the theme of the app. If a preferred theme is not set in * A custom hook to manage the theme of the app.
* localStorage, it will use what the user's browser prefers as determined by the
* prefers-color-scheme media query. If the user changes their preferred theme, it will be
* saved in localStorage and used in subsequent visits.
* *
* @returns ThemeState.theme - The current theme of the app. * If a preferred theme is not set in localStorage, it will use what the user's browser
* @returns ThemeState.setTheme - A setter function to change the theme of the app. * prefers as determined by the prefers-color-scheme media query.
*
* If the user changes their preferred theme, it will be saved in localStorage and used in
* subsequent visits.
*
* @returns An object with the following properties:
*
* - `theme`: The current theme of the app.
* - `setTheme`: A function to set the theme of the app.
*/ */
const useTheme = () => { const useTheme = () => {
const [theme, setTheme] = useState<'light' | 'dark'>('light'); const [theme, setTheme] = useState<'light' | 'dark'>('light');

View File

@@ -2,12 +2,17 @@ import formatDistanceStrict from 'date-fns/formatDistanceStrict';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
/** /**
* Returns the time distance between the provided date and the current time, using the * A custom hook to calculate the time distance between the provided date and the current
* `date-fns` `formatDistanceStrict` function. This hook ensures that the same result is * time.
* calculated on both the server and client, preventing hydration errors. *
* This hook uses the date-fns library to calculate the time distance.
*
* This hook ensures that the same result is calculated on both the server and client,
* preventing hydration errors.
* *
* @param createdAt The date to calculate the time distance from. * @param createdAt The date to calculate the time distance from.
* @returns The time distance between the provided date and the current time. * @returns The time distance between the provided date and the current time.
* @see https://date-fns.org/v2.30.0/docs/formatDistanceStrict
*/ */
const useTimeDistance = (createdAt: Date) => { const useTimeDistance = (createdAt: Date) => {
const [timeDistance, setTimeDistance] = useState(''); const [timeDistance, setTimeDistance] = useState('');