在哪里可以实施SWRS分页以管理URL的分页?
我一直在试图解决此问题的问题,即此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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
由于您的全局缓存应该考虑当前页面来管理您的数据,因此该数据应存在于密钥中,然后您必须像
使用wringwithtoken一样通过钩子发送(``/deration/'page $ {currentpage}`)
,然后每次当前页面更改时,SWR都会触发新的获取。我建议不要将字符串用作键,而是建议将数组与所有数据一样
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