在哪里可以实施SWRS分页以管理URL的分页?

发布于 2025-01-26 03:28:22 字数 16592 浏览 2 评论 0原文

我一直在试图解决此问题的问题,即此NextJS应用程序在后端上没有任何分页,因此想法是将其传递给URL中的参数,因此localhost:3000/dertaent?page = 。

我采用了这种方法:

import React, { useEffect } from 'react'
import PatientsTable from 'components/patients/PatientsTable'
import useSWRWithToken from 'hooks/useSWRWithToken'
import Feedback from 'components/feedback'
import { useRouter } from 'next/router'

function Patients(props) {
  const { data: patientsList, error: patientsListError } =
    useSWRWithToken('/patients')
  const router = useRouter()
  const { page, rowsPerPage, onPageChange, query } = props

  useEffect(() => {
    const { pg } = props
    const nextPage = parseInt(pg)
    if (page !== nextPage) {
      router.replace({
        query: {
          ...router.query,
          pg: page,
        },
      })
    }
  }, [page, query, router, router.replace])
  return (
    <>
      <Feedback />
      <PatientsTable
        patientsList={patientsList}
        patientsListError={patientsListError}
      />
    </>
  )
}

Patients.layout = 'fullScreen'
Patients.auth = true
export default Patients

但是事件处理程序要转到下一个和上一个页面停止工作:

 import React from 'react'
import { IconButton } from '@mui/material'
import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight'
import { styled, useTheme } from '@mui/system'

const Root = styled('div')(({ theme }) => ({
  flexShrink: 0,
  marginLeft: theme.spacing(2.5),
}))

const TablePaginationActions = (props) => {
  const theme = useTheme()
  const { count, page, rowsPerPage, onPageChange } = props

  const handleFirstPageButtonClick = (event) => {
    onPageChange(event, 0)
  }

  const handleBackButtonClick = (event) => {
    onPageChange(event, page - 1)
  }

  const handleNextButtonClick = (event) => {
    onPageChange(event, page + 1)
  }

  const handleLastPageButtonClick = (event) => {
    onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1))
  }

  return (
    <Root>
      <IconButton
        onClick={handleBackButtonClick}
        disabled={page === 0}
        aria-label="previous page"
        data-cy={'table-pagination-actions-icon-button-prev'}
      >
        {theme.direction === 'rtl' ? (
          <KeyboardArrowRight />
        ) : (
          <KeyboardArrowLeft />
        )}
      </IconButton>
      <IconButton
        onClick={handleNextButtonClick}
        disabled={page >= Math.ceil(count / rowsPerPage) - 1}
        aria-label="next page"
        data-cy={'table-pagination-actions-icon-button-next'}
      >
        {theme.direction === 'rtl' ? (
          <KeyboardArrowLeft />
        ) : (
          <KeyboardArrowRight />
        )}
      </IconButton>
    </Root>
  )
}

export default TablePaginationActions

我想说这是因为我的使用 hook被某种程度上被使用使用钩子,但我无法证明这一点。

我正在尝试实现以下内容:

https://swr.vercel.app/docs/docs/pagination

这是用途:

import useSWR from 'swr'
import fetchWithToken from 'libs/fetchWithToken'
import { useAppContext } from 'context'

function useSWRWithToken(endpoint: any, dependency: Boolean = true) {
  const { state } = useAppContext()
  const {
    token: { accessToken },
  } = state

  const { data, error, mutate } = useSWR<any>(
    // Make sure there is an accessToken and null does not appear in the uri
    accessToken && endpoint.indexOf(null) === -1 && dependency
      ? endpoint
      : null,

    (url: string, params: object = null) =>
      fetchWithToken(url, accessToken, params)
  )

  return {
    data,
    isLoading: !error && !data,
    error,
    mutate,
  }
}

export default useSWRWithToken

这是患者表组件:

import React, {
  useEffect,
  useState,
  useMemo,
  useCallback,
  ReactChild,
} from 'react'
import { Paper } from '@mui/material'
import { LinearLoader } from 'components/loaders'
import { useRouter } from 'next/router'
import GeneralTable from 'components/table/GeneralTable'
import { parseInt, isNil } from 'lodash'
import MultiSelectChip, {
  ChipCommonFilter,
} from 'components/forms/MultiSelectChip'
import usePermissions from 'hooks/usePermissions'
import { differenceInDays } from 'date-fns'

import { useAppContext } from 'context'

import { PatientsTableColumns } from 'components/table/patients/PatientsTableColumns'
import ProviderSelect from 'components/patients/ProviderSelect'

// TODO: declare interface types
interface IPatientList {
  patientsList: any
  patientsListError?: any
}

function PatientsTable({
  patientsList = null,
  patientsListError,
}: IPatientList) {
  const router = useRouter()
  const { state, dispatch } = useAppContext()
  const { controlLevelFilters, selectedProviderFilter } = state.patientSearch
  const [providerList, setProviderList] = useState<Array<string>>([])

  const [dataParsed, setDataParsed] = useState([])
  const [controlFilterOptions, setControlFilterOptions] = useState(['all'])
  const [scroll, setScroll] = useState(false)
  const { permissionPublicUserId }: { permissionPublicUserId: boolean } =
    usePermissions()

  const setControlLevelFilters = (value: string[]) => {
    dispatch({
      type: 'SET_CONTROL_LEVEL_FILTERS',
      payload: {
        controlLevelFilters: value,
      },
    })
  }
  const setSelectedProviderFilter = (provider: string) => {
    console.log('provider: ', provider)
    dispatch({
      type: 'SET_SELECTED_PROVIDER_FILTER',
      payload: {
        selectedProviderFilter: provider,
      },
    })
  }

  const setSortState = (memoColumn: string, memoDirection: string) => {
    dispatch({
      type: 'SET_PATIENT_TABLE_SORT',
      payload: {
        columnName: memoColumn,
        direction: !memoDirection ? 'asc' : 'desc',
      },
    })
  }

  const handleChangeControlLevelFilter = (
    event: any,
    child: ReactChild,
    deleteValue: string
  ) => {
    ChipCommonFilter(
      event,
      child,
      deleteValue,
      setControlLevelFilters,
      controlLevelFilters
    )
  }

  useEffect(() => {
    dispatch({
      type: 'SET_PAGE_HEADING',
      payload: {
        pageHeading1: 'Patients',
        pageHeading2: `${
          patientsList?.length ? `(${patientsList.length})` : ''
        }`,
      },
    })
  }, [patientsList])

  useEffect(() => {
    // Build up a list of patient objects which our table can traverse
    const dataParsed = patientsList?.map((row) => {
      !isNil(row.doctorDto) &&
        setProviderList((previousProviderList) => [
          ...previousProviderList,
          `${row.doctorDto.firstName} ${row.doctorDto.lastName}`,
        ])

      const reportedDate = row.scalarReports.filter(
        (obj) => obj.name === 'lastUseInDays'
      )[0]?.reportedOn

      const diffDate: number = Math.abs(
        differenceInDays(new Date(reportedDate), new Date())
      )
      const lastUsed: string | number =
        diffDate > 7
          ? diffDate
          : diffDate === 0 && !isNil(reportedDate)
          ? 'Today'
          : isNil(reportedDate)
          ? '--'
          : diffDate

      return {
        pui: row.pui,
        provider: !isNil(row.doctorDto)
          ? `${row.doctorDto.firstName} ${row.doctorDto.lastName}`
          : '',
        name: `${row.firstName} ${row.lastName}`,
        dob: row.dob,
        asthmaControl: row.scalarReports.filter(
          (obj) => obj.name === 'asthmaControl'
        )[0]?.value,
        lastUsed,
        fev1Baseline: row.scalarReports.filter(
          (obj) => obj.name === 'fevBaseLine'
        )[0]?.value,
      }
    })
    setDataParsed(dataParsed)
  }, [patientsList])

  useEffect(() => {
    window.addEventListener('scroll', () => {
      setScroll(window.scrollY > 50)
    })
  }, [])

  useEffect(() => {
    if (dataParsed) {
      setControlFilterOptions([
        'all',
        ...(dataParsed.find((patient) => patient.asthmaControl === 'good')
          ? ['good']
          : []),
        ...(dataParsed.find((patient) => patient.asthmaControl === 'poor')
          ? ['poor']
          : []),
        ...(dataParsed.find((patient) => patient.asthmaControl === 'veryPoor')
          ? ['veryPoor']
          : []),
      ])
    }
  }, [dataParsed])

  const handleSelectProvider = (provider) => setSelectedProviderFilter(provider)

  const isToday = (val) => val === 'Today' || val === 'today'
  const isInactive = (val) => val === 'Inactive' || val === 'inactive'
  const isDash = (val) => isNil(val) || val === '--'
  const isAsthmaControl = (val) => {
    const _val = val?.toString().toLowerCase()
    let result: Number | boolean = false
    switch (_val) {
      case 'verypoor':
        result = 1
        break
      case 'poor':
        result = 2
        break
      case 'good':
        result = 3
        break
      default:
        result = false
    }
    return result
  }

  const CustomSortBy = useCallback(
    (rowA, rowB, colId, direction) => {
      const convertValue = (val) => {
        if (isToday(val)) return 0 //Today != 1
        if (isInactive(val)) return direction === false ? -2 : 29999
        if (isDash(val)) return direction === false ? -3 : 30000

        const acResult = isAsthmaControl(val) //so we don't call it twice
        if (acResult) return acResult

        return parseInt(val)
      }

      const v1 = convertValue(rowA.values[colId])
      const v2 = convertValue(rowB.values[colId])
      return v1 >= v2 ? 1 : -1 // Direction var doesn't matter.
    },
    [patientsList, isToday, isInactive, isDash]
  )

  const columns = useMemo(() => PatientsTableColumns(CustomSortBy), [])

  const rowLinkOnClick = (patient) => {
    router.push(`/patients/${patient.pui}`)
  }

  // TODO put better error here
  if (patientsListError) return <div>Failed to load patients list</div>

  if (!patientsList) return <LinearLoader />

  return (
    <Paper elevation={0} square>
      {patientsList && dataParsed && (
        <GeneralTable
          retainSortState={true}
          sortStateRecorder={setSortState}
          columns={columns}
          data={dataParsed}
          checkRows={false}
          rowLinkOnClick={rowLinkOnClick}
          filters={[
            {
              column: 'asthmaControl',
              options: controlLevelFilters,
            },
            {
              column: 'provider',
              options: selectedProviderFilter,
            },
          ]}
          initialState={{
            sortBy: [
              {
                id: state.patientTableSort.columnName,
                desc: state.patientTableSort.direction === 'desc',
              },
            ],
            hiddenColumns: false && !permissionPublicUserId ? [] : ['pui'], //For now, intentionally always hide
          }}
          leftContent={
            <div style={{ display: 'flex' }}>
              <MultiSelectChip
                labelText="Asthma Control"
                options={controlFilterOptions}
                selectedValues={controlLevelFilters}
                handleChange={handleChangeControlLevelFilter}
              />
              <ProviderSelect
                providers={Array.from(new Set(providerList))}
                providerFilter={selectedProviderFilter}
                handleChange={handleSelectProvider}
              />
            </div>
          }
        />
      )}
    </Paper>
  )
}

export default PatientsTable

我还应该提到,全球状态属于批准的这样的人:

import combineReducers from 'react-combine-reducers'
import { ReactElement } from 'react'
enum ActionName {
  SET_PAGE_HEADING = 'SET_PAGE_HEADING',
  SET_TIMER_RUNNING = 'SET_TIMER_RUNNING',
  SET_PATIENT_DATA = 'SET_PATIENT_DATA',
  SET_CHART_PERIOD = 'SET_CHART_PERIOD',
  SET_TOKEN = 'SET_TOKEN',
}

enum ChartPeriod {
  WEEK = 'week',
  THREE_MONTH = '1m',
  ONE_MONTH = 'week',
}

type Action = {
  type: string
  payload: any
}

// Types for global App State
type PageHeadingState = {
  pageHeading1: string
  pageHeading2?: string
  component?: ReactElement
}

// TODO: fill this in or reference existing type
// types like this need to be centralized
type Patient = object

type PatientDataState = [
  {
    [key: string]: Patient
  }
]

type PatientSearch = {
  controlLevelFilters: string[]
  selectedProviderFilter: string[]
}

type TimerState = {
  running: boolean
  visitId?: string | null
  stopTimer?: () => void
}

type AppState = {
  pageHeading: PageHeadingState
  timer: TimerState

  // TODO: flesh out what the shape of this data is and type it
  // once the swagger definition is complete (state: PatientDataState)
  patientData: PatientDataState
  chartPeriod: ChartPeriod
  patientSearch: PatientSearch
  patientTableSort: PatientTableSort
  token: object
}

// A reducer type to aggregate (n) reducers
type AppStateReducer = (state: AppState, action: Action) => AppState

// Initial State for the app
const initialPageHeading = {
  pageHeading1: '',
  pageHeading2: '',
}

const initialTimer = {
  running: false,
}

const initialChartPeriod = ChartPeriod.WEEK

const initialPatientData: PatientDataState = [{}]

const initialPatientSearch: PatientSearch = {
  controlLevelFilters: ['all'],
  selectedProviderFilter: ['All Providers'],
}

type PatientTableSort = { columnName: string; direction: 'asc' | 'desc' }

const initialPatientTableSort: PatientTableSort = {
  columnName: 'asthmaControl',
  direction: 'desc',
}

// Perhaps can make this more explicit with STOP_PATIENT_CARE_TIMER
// and STOP_PATIENT_CARE_TIMER action cases
// I have kept the CRUD to a minimum for this first POC
const timerReducer = (state: TimerState, action: Action) => {
  switch (action.type) {
    case ActionName.SET_TIMER_RUNNING:
      return { ...state, ...action.payload }
    // case ActionName.STOP_PATIENT_CARE_TIMER:
    //   return { running: false }
    default:
      return state
  }
}

const pageHeadingReducer = (state: PageHeadingState, action: Action) => {
  switch (action.type) {
    case ActionName.SET_PAGE_HEADING: {
      return {
        ...state,
        ...action.payload,
      }
    }
    default:
      return state
  }
}

// TODO: flesh out what the shape of this data is and type it
// once the swagger definition is complete (state: PatientDataState)
const patientDataReducer = (state: PatientDataState, action) => {
  switch (action.type) {
    case ActionName.SET_PATIENT_DATA: {
      return action.payload
    }
    default:
      return state
  }
}

const chartPeriodReducer = (state: ChartPeriod, action: Action) => {
  switch (action.type) {
    case ActionName.SET_CHART_PERIOD: {
      return action.payload
    }
    default:
      return state
  }
}

const patientSearchReducer = (state: PatientSearch, action: Action) => {
  switch (action.type) {
    case 'SET_SELECTED_PROVIDER_FILTER': {
      return { ...state, ...action.payload }
    }
    case 'SET_CONTROL_LEVEL_FILTERS': {
      return { ...state, ...action.payload }
    }
    default:
      return state
  }
}

const patientTableSortReducer = (state: PatientTableSort, action: Action) => {
  switch (action.type) {
    case 'SET_PATIENT_TABLE_SORT': {
      return { ...state, ...action.payload }
    }
    case 'CLEAR_PATIENT_TABLE_SORT': {
      const update = { columnName: '', direction: 'asc' }
      return { ...state, ...update }
    }
    default:
      return state
  }
}

const tokenReducer = (state: object, action: Action) => {
  switch (action.type) {
    case 'SET_TOKEN': {
      return action.payload
    }
    default:
      return state
  }
}

// This is exposed for use in AppContext.tsx so bootstrap our store/state
export const [AppReducer, initialState] = combineReducers<AppStateReducer>({
  pageHeading: [pageHeadingReducer, initialPageHeading],
  timer: [timerReducer, initialTimer],
  patientData: [patientDataReducer, initialPatientData],
  chartPeriod: [chartPeriodReducer, initialChartPeriod],
  patientSearch: [patientSearchReducer, initialPatientSearch],
  patientTableSort: [patientTableSortReducer, initialPatientTableSort],
  token: [tokenReducer, {}],
})

I have been trying to solve the problem that this NextJS application does not have any pagination being handled on the backend so the idea being to pass it to query params in the url, so localhost:3000/patients?page=.

I came close with this approach:

import React, { useEffect } from 'react'
import PatientsTable from 'components/patients/PatientsTable'
import useSWRWithToken from 'hooks/useSWRWithToken'
import Feedback from 'components/feedback'
import { useRouter } from 'next/router'

function Patients(props) {
  const { data: patientsList, error: patientsListError } =
    useSWRWithToken('/patients')
  const router = useRouter()
  const { page, rowsPerPage, onPageChange, query } = props

  useEffect(() => {
    const { pg } = props
    const nextPage = parseInt(pg)
    if (page !== nextPage) {
      router.replace({
        query: {
          ...router.query,
          pg: page,
        },
      })
    }
  }, [page, query, router, router.replace])
  return (
    <>
      <Feedback />
      <PatientsTable
        patientsList={patientsList}
        patientsListError={patientsListError}
      />
    </>
  )
}

Patients.layout = 'fullScreen'
Patients.auth = true
export default Patients

but the event handlers to go to the next and previous pages stopped working:

 import React from 'react'
import { IconButton } from '@mui/material'
import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight'
import { styled, useTheme } from '@mui/system'

const Root = styled('div')(({ theme }) => ({
  flexShrink: 0,
  marginLeft: theme.spacing(2.5),
}))

const TablePaginationActions = (props) => {
  const theme = useTheme()
  const { count, page, rowsPerPage, onPageChange } = props

  const handleFirstPageButtonClick = (event) => {
    onPageChange(event, 0)
  }

  const handleBackButtonClick = (event) => {
    onPageChange(event, page - 1)
  }

  const handleNextButtonClick = (event) => {
    onPageChange(event, page + 1)
  }

  const handleLastPageButtonClick = (event) => {
    onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1))
  }

  return (
    <Root>
      <IconButton
        onClick={handleBackButtonClick}
        disabled={page === 0}
        aria-label="previous page"
        data-cy={'table-pagination-actions-icon-button-prev'}
      >
        {theme.direction === 'rtl' ? (
          <KeyboardArrowRight />
        ) : (
          <KeyboardArrowLeft />
        )}
      </IconButton>
      <IconButton
        onClick={handleNextButtonClick}
        disabled={page >= Math.ceil(count / rowsPerPage) - 1}
        aria-label="next page"
        data-cy={'table-pagination-actions-icon-button-next'}
      >
        {theme.direction === 'rtl' ? (
          <KeyboardArrowLeft />
        ) : (
          <KeyboardArrowRight />
        )}
      </IconButton>
    </Root>
  )
}

export default TablePaginationActions

I want to say it's because my useEffect hook was being somehow clobbered by the useSWR hook but I cannot prove it.

I am trying to implement the following:

https://swr.vercel.app/docs/pagination

This is the useSWRWithToken:

import useSWR from 'swr'
import fetchWithToken from 'libs/fetchWithToken'
import { useAppContext } from 'context'

function useSWRWithToken(endpoint: any, dependency: Boolean = true) {
  const { state } = useAppContext()
  const {
    token: { accessToken },
  } = state

  const { data, error, mutate } = useSWR<any>(
    // Make sure there is an accessToken and null does not appear in the uri
    accessToken && endpoint.indexOf(null) === -1 && dependency
      ? endpoint
      : null,

    (url: string, params: object = null) =>
      fetchWithToken(url, accessToken, params)
  )

  return {
    data,
    isLoading: !error && !data,
    error,
    mutate,
  }
}

export default useSWRWithToken

This is the Patients Table component:

import React, {
  useEffect,
  useState,
  useMemo,
  useCallback,
  ReactChild,
} from 'react'
import { Paper } from '@mui/material'
import { LinearLoader } from 'components/loaders'
import { useRouter } from 'next/router'
import GeneralTable from 'components/table/GeneralTable'
import { parseInt, isNil } from 'lodash'
import MultiSelectChip, {
  ChipCommonFilter,
} from 'components/forms/MultiSelectChip'
import usePermissions from 'hooks/usePermissions'
import { differenceInDays } from 'date-fns'

import { useAppContext } from 'context'

import { PatientsTableColumns } from 'components/table/patients/PatientsTableColumns'
import ProviderSelect from 'components/patients/ProviderSelect'

// TODO: declare interface types
interface IPatientList {
  patientsList: any
  patientsListError?: any
}

function PatientsTable({
  patientsList = null,
  patientsListError,
}: IPatientList) {
  const router = useRouter()
  const { state, dispatch } = useAppContext()
  const { controlLevelFilters, selectedProviderFilter } = state.patientSearch
  const [providerList, setProviderList] = useState<Array<string>>([])

  const [dataParsed, setDataParsed] = useState([])
  const [controlFilterOptions, setControlFilterOptions] = useState(['all'])
  const [scroll, setScroll] = useState(false)
  const { permissionPublicUserId }: { permissionPublicUserId: boolean } =
    usePermissions()

  const setControlLevelFilters = (value: string[]) => {
    dispatch({
      type: 'SET_CONTROL_LEVEL_FILTERS',
      payload: {
        controlLevelFilters: value,
      },
    })
  }
  const setSelectedProviderFilter = (provider: string) => {
    console.log('provider: ', provider)
    dispatch({
      type: 'SET_SELECTED_PROVIDER_FILTER',
      payload: {
        selectedProviderFilter: provider,
      },
    })
  }

  const setSortState = (memoColumn: string, memoDirection: string) => {
    dispatch({
      type: 'SET_PATIENT_TABLE_SORT',
      payload: {
        columnName: memoColumn,
        direction: !memoDirection ? 'asc' : 'desc',
      },
    })
  }

  const handleChangeControlLevelFilter = (
    event: any,
    child: ReactChild,
    deleteValue: string
  ) => {
    ChipCommonFilter(
      event,
      child,
      deleteValue,
      setControlLevelFilters,
      controlLevelFilters
    )
  }

  useEffect(() => {
    dispatch({
      type: 'SET_PAGE_HEADING',
      payload: {
        pageHeading1: 'Patients',
        pageHeading2: `${
          patientsList?.length ? `(${patientsList.length})` : ''
        }`,
      },
    })
  }, [patientsList])

  useEffect(() => {
    // Build up a list of patient objects which our table can traverse
    const dataParsed = patientsList?.map((row) => {
      !isNil(row.doctorDto) &&
        setProviderList((previousProviderList) => [
          ...previousProviderList,
          `${row.doctorDto.firstName} ${row.doctorDto.lastName}`,
        ])

      const reportedDate = row.scalarReports.filter(
        (obj) => obj.name === 'lastUseInDays'
      )[0]?.reportedOn

      const diffDate: number = Math.abs(
        differenceInDays(new Date(reportedDate), new Date())
      )
      const lastUsed: string | number =
        diffDate > 7
          ? diffDate
          : diffDate === 0 && !isNil(reportedDate)
          ? 'Today'
          : isNil(reportedDate)
          ? '--'
          : diffDate

      return {
        pui: row.pui,
        provider: !isNil(row.doctorDto)
          ? `${row.doctorDto.firstName} ${row.doctorDto.lastName}`
          : '',
        name: `${row.firstName} ${row.lastName}`,
        dob: row.dob,
        asthmaControl: row.scalarReports.filter(
          (obj) => obj.name === 'asthmaControl'
        )[0]?.value,
        lastUsed,
        fev1Baseline: row.scalarReports.filter(
          (obj) => obj.name === 'fevBaseLine'
        )[0]?.value,
      }
    })
    setDataParsed(dataParsed)
  }, [patientsList])

  useEffect(() => {
    window.addEventListener('scroll', () => {
      setScroll(window.scrollY > 50)
    })
  }, [])

  useEffect(() => {
    if (dataParsed) {
      setControlFilterOptions([
        'all',
        ...(dataParsed.find((patient) => patient.asthmaControl === 'good')
          ? ['good']
          : []),
        ...(dataParsed.find((patient) => patient.asthmaControl === 'poor')
          ? ['poor']
          : []),
        ...(dataParsed.find((patient) => patient.asthmaControl === 'veryPoor')
          ? ['veryPoor']
          : []),
      ])
    }
  }, [dataParsed])

  const handleSelectProvider = (provider) => setSelectedProviderFilter(provider)

  const isToday = (val) => val === 'Today' || val === 'today'
  const isInactive = (val) => val === 'Inactive' || val === 'inactive'
  const isDash = (val) => isNil(val) || val === '--'
  const isAsthmaControl = (val) => {
    const _val = val?.toString().toLowerCase()
    let result: Number | boolean = false
    switch (_val) {
      case 'verypoor':
        result = 1
        break
      case 'poor':
        result = 2
        break
      case 'good':
        result = 3
        break
      default:
        result = false
    }
    return result
  }

  const CustomSortBy = useCallback(
    (rowA, rowB, colId, direction) => {
      const convertValue = (val) => {
        if (isToday(val)) return 0 //Today != 1
        if (isInactive(val)) return direction === false ? -2 : 29999
        if (isDash(val)) return direction === false ? -3 : 30000

        const acResult = isAsthmaControl(val) //so we don't call it twice
        if (acResult) return acResult

        return parseInt(val)
      }

      const v1 = convertValue(rowA.values[colId])
      const v2 = convertValue(rowB.values[colId])
      return v1 >= v2 ? 1 : -1 // Direction var doesn't matter.
    },
    [patientsList, isToday, isInactive, isDash]
  )

  const columns = useMemo(() => PatientsTableColumns(CustomSortBy), [])

  const rowLinkOnClick = (patient) => {
    router.push(`/patients/${patient.pui}`)
  }

  // TODO put better error here
  if (patientsListError) return <div>Failed to load patients list</div>

  if (!patientsList) return <LinearLoader />

  return (
    <Paper elevation={0} square>
      {patientsList && dataParsed && (
        <GeneralTable
          retainSortState={true}
          sortStateRecorder={setSortState}
          columns={columns}
          data={dataParsed}
          checkRows={false}
          rowLinkOnClick={rowLinkOnClick}
          filters={[
            {
              column: 'asthmaControl',
              options: controlLevelFilters,
            },
            {
              column: 'provider',
              options: selectedProviderFilter,
            },
          ]}
          initialState={{
            sortBy: [
              {
                id: state.patientTableSort.columnName,
                desc: state.patientTableSort.direction === 'desc',
              },
            ],
            hiddenColumns: false && !permissionPublicUserId ? [] : ['pui'], //For now, intentionally always hide
          }}
          leftContent={
            <div style={{ display: 'flex' }}>
              <MultiSelectChip
                labelText="Asthma Control"
                options={controlFilterOptions}
                selectedValues={controlLevelFilters}
                handleChange={handleChangeControlLevelFilter}
              />
              <ProviderSelect
                providers={Array.from(new Set(providerList))}
                providerFilter={selectedProviderFilter}
                handleChange={handleSelectProvider}
              />
            </div>
          }
        />
      )}
    </Paper>
  )
}

export default PatientsTable

I should also mention that global state comes into an appReducer like so:

import combineReducers from 'react-combine-reducers'
import { ReactElement } from 'react'
enum ActionName {
  SET_PAGE_HEADING = 'SET_PAGE_HEADING',
  SET_TIMER_RUNNING = 'SET_TIMER_RUNNING',
  SET_PATIENT_DATA = 'SET_PATIENT_DATA',
  SET_CHART_PERIOD = 'SET_CHART_PERIOD',
  SET_TOKEN = 'SET_TOKEN',
}

enum ChartPeriod {
  WEEK = 'week',
  THREE_MONTH = '1m',
  ONE_MONTH = 'week',
}

type Action = {
  type: string
  payload: any
}

// Types for global App State
type PageHeadingState = {
  pageHeading1: string
  pageHeading2?: string
  component?: ReactElement
}

// TODO: fill this in or reference existing type
// types like this need to be centralized
type Patient = object

type PatientDataState = [
  {
    [key: string]: Patient
  }
]

type PatientSearch = {
  controlLevelFilters: string[]
  selectedProviderFilter: string[]
}

type TimerState = {
  running: boolean
  visitId?: string | null
  stopTimer?: () => void
}

type AppState = {
  pageHeading: PageHeadingState
  timer: TimerState

  // TODO: flesh out what the shape of this data is and type it
  // once the swagger definition is complete (state: PatientDataState)
  patientData: PatientDataState
  chartPeriod: ChartPeriod
  patientSearch: PatientSearch
  patientTableSort: PatientTableSort
  token: object
}

// A reducer type to aggregate (n) reducers
type AppStateReducer = (state: AppState, action: Action) => AppState

// Initial State for the app
const initialPageHeading = {
  pageHeading1: '',
  pageHeading2: '',
}

const initialTimer = {
  running: false,
}

const initialChartPeriod = ChartPeriod.WEEK

const initialPatientData: PatientDataState = [{}]

const initialPatientSearch: PatientSearch = {
  controlLevelFilters: ['all'],
  selectedProviderFilter: ['All Providers'],
}

type PatientTableSort = { columnName: string; direction: 'asc' | 'desc' }

const initialPatientTableSort: PatientTableSort = {
  columnName: 'asthmaControl',
  direction: 'desc',
}

// Perhaps can make this more explicit with STOP_PATIENT_CARE_TIMER
// and STOP_PATIENT_CARE_TIMER action cases
// I have kept the CRUD to a minimum for this first POC
const timerReducer = (state: TimerState, action: Action) => {
  switch (action.type) {
    case ActionName.SET_TIMER_RUNNING:
      return { ...state, ...action.payload }
    // case ActionName.STOP_PATIENT_CARE_TIMER:
    //   return { running: false }
    default:
      return state
  }
}

const pageHeadingReducer = (state: PageHeadingState, action: Action) => {
  switch (action.type) {
    case ActionName.SET_PAGE_HEADING: {
      return {
        ...state,
        ...action.payload,
      }
    }
    default:
      return state
  }
}

// TODO: flesh out what the shape of this data is and type it
// once the swagger definition is complete (state: PatientDataState)
const patientDataReducer = (state: PatientDataState, action) => {
  switch (action.type) {
    case ActionName.SET_PATIENT_DATA: {
      return action.payload
    }
    default:
      return state
  }
}

const chartPeriodReducer = (state: ChartPeriod, action: Action) => {
  switch (action.type) {
    case ActionName.SET_CHART_PERIOD: {
      return action.payload
    }
    default:
      return state
  }
}

const patientSearchReducer = (state: PatientSearch, action: Action) => {
  switch (action.type) {
    case 'SET_SELECTED_PROVIDER_FILTER': {
      return { ...state, ...action.payload }
    }
    case 'SET_CONTROL_LEVEL_FILTERS': {
      return { ...state, ...action.payload }
    }
    default:
      return state
  }
}

const patientTableSortReducer = (state: PatientTableSort, action: Action) => {
  switch (action.type) {
    case 'SET_PATIENT_TABLE_SORT': {
      return { ...state, ...action.payload }
    }
    case 'CLEAR_PATIENT_TABLE_SORT': {
      const update = { columnName: '', direction: 'asc' }
      return { ...state, ...update }
    }
    default:
      return state
  }
}

const tokenReducer = (state: object, action: Action) => {
  switch (action.type) {
    case 'SET_TOKEN': {
      return action.payload
    }
    default:
      return state
  }
}

// This is exposed for use in AppContext.tsx so bootstrap our store/state
export const [AppReducer, initialState] = combineReducers<AppStateReducer>({
  pageHeading: [pageHeadingReducer, initialPageHeading],
  timer: [timerReducer, initialTimer],
  patientData: [patientDataReducer, initialPatientData],
  chartPeriod: [chartPeriodReducer, initialChartPeriod],
  patientSearch: [patientSearchReducer, initialPatientSearch],
  patientTableSort: [patientTableSortReducer, initialPatientTableSort],
  token: [tokenReducer, {}],
})

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

柠檬 2025-02-02 03:28:22

由于您的全局缓存应该考虑当前页面来管理您的数据,因此该数据应存在于密钥中,然后您必须像使用wringwithtoken一样通过钩子发送(``/deration/'page $ {currentpage}`),然后每次当前页面更改时,SWR都会触发新的获取。
我建议不要将字符串用作键,而是建议将数组与所有数据一样


const fetcher = async (key: string, id: string, pagination: number) => {
  //He in your fetch you'll receive the key ordered by the array
  fetchWithToken(key, accessToken, [id, page: pagination])
}
const useSWRWithToken = (key, id, pagination) = {
      const { data, error, isValidating } = useSWR(
        [`patients/${id}`, id, pagination], fetcher, options
      )
  }

Since your global cache should consider the current page to manage your data, this data should be present in the key, then you have to send it through your hook like useSWRWithToken(`/patients/?page${currentPage}`) and then every time the current page changes SWR will trigger a new fetch.
Instead of using string as key, I recommend using an array with all your data like


const fetcher = async (key: string, id: string, pagination: number) => {
  //He in your fetch you'll receive the key ordered by the array
  fetchWithToken(key, accessToken, [id, page: pagination])
}
const useSWRWithToken = (key, id, pagination) = {
      const { data, error, isValidating } = useSWR(
        [`patients/${id}`, id, pagination], fetcher, options
      )
  }

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文