exercism/website

View on GitHub
app/javascript/hooks/use-list.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import { useReducer, useCallback, Reducer } from 'react'
import { Request } from './request-query'

export type ListState = Request & {
  query: {
    page?: number
    videoPage?: number
    criteria?: string
    tags?: string[]
    trackSlug?: string
  }
}

type ListAction =
  | {
      type: 'page.changed'
      payload: { page: number } | { [key: string]: number }
    }
  | { type: 'query.changed'; payload: { query: { page: number } } }
  | { type: 'criteria.changed'; payload: { criteria: string } }
  | { type: 'filter.changed'; payload: { filter: string } }
  | { type: 'order.changed'; payload: { order: string } }
  | { type: '' }

const reducer: Reducer<ListState, ListAction> = (
  state: ListState,
  action: ListAction
) => {
  switch (action.type) {
    case 'page.changed':
      return { ...state, query: { ...state.query, page: action.payload.page } }
    case 'query.changed':
      return { ...state, query: { ...action.payload.query } }
    case 'criteria.changed':
      return {
        ...state,
        query: {
          ...state.query,
          criteria: action.payload.criteria,
        },
      }
    case 'filter.changed':
      return {
        ...state,
        query: {
          ...state.query,
          filter: action.payload.filter,
        },
      }
    case 'order.changed':
      return {
        ...state,
        query: { ...state.query, order: action.payload.order },
      }
    default:
      if (process.env.NODE_ENV === 'development') {
        throw new Error(`Unknown action type: ${action.type}`)
      }
      return state
  }
}

export function useList(initialRequest: Request): {
  request: ListState
  setCriteria: (criteria: string) => void
  setOrder: (order: string) => void
  setPage: (page: number, key?: string) => void
  setQuery: (query: any) => void
  setFilter: (filter: string) => void
} {
  const [request, dispatch] = useReducer(
    reducer,
    Object.assign({ query: { page: 1 } }, initialRequest)
  )

  const setCriteria = useCallback(
    (criteria: string) => {
      dispatch({ type: 'criteria.changed', payload: { criteria } })
    },
    [dispatch]
  )

  const setPage = useCallback(
    (page: number, key = 'page') => {
      dispatch({ type: 'page.changed', payload: { [key]: page } })
    },
    [dispatch]
  )

  const setOrder = useCallback(
    (order: string) => {
      dispatch({ type: 'order.changed', payload: { order } })
    },
    [dispatch]
  )

  const setFilter = useCallback(
    (filter: string) => {
      dispatch({ type: 'filter.changed', payload: { filter } })
    },
    [dispatch]
  )

  const setQuery = useCallback(
    (query) => {
      dispatch({ type: 'query.changed', payload: { query } })
    },
    [dispatch]
  )

  return {
    request,
    setCriteria,
    setOrder,
    setPage,
    setQuery,
    setFilter,
  }
}