I have a project, and this project contains several interfaces, and among these interfaces there is an interface in order to display the medical plan shown in the image, and as shown in the image, the medical plan contains several sections, and the appearance of these sections differs from one role to another,
When I log in with a specific role account, let it be “Cobalt”, since Cobalt is not allowed to see the rest of the sections, it is only allowed to see the Cobalt section, and in order not to call the api for the other sections, I used:
useState
enabled
setState
But I got this error,
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
How can I solve the problem?
import { Col, Collapse, Row, Tag } from 'antd';
import {
FunctionComponent, useContext, useMemo, useState,
} from 'react';
import { FormattedMessage } from 'react-intl';
import { useQueries } from 'react-query';
import { useParams } from 'react-router';
import analysis from '../../../../../api/nuclearMedicineApi/services/Analysis';
import examination from '../../../../../api/nuclearMedicineApi/services/Examination';
import radialImage from '../../../../../api/nuclearMedicineApi/services/RadialImageByXRay';
import radialImageByMRI from '../../../../../api/nuclearMedicineApi/services/RadialImageByMRI'
import radioTherapyByCesium from '../../../../../api/nuclearMedicineApi/services/RadioTherapyByCesium';
import radioTherapyByCobalt from '../../../../../api/nuclearMedicineApi/services/RadioTherapyByCobalt';
import radioTherapyByIodine from '../../../../../api/nuclearMedicineApi/services/RadioTherapyByIodine';
import radioTherapyByLinac from '../../../../../api/nuclearMedicineApi/services/RadioTherapyByLinac';
import { IFormProps } from '../../../../../interfaces/form-props';
import FormElement from '../../../../common/form-element';
import AnalysisPage from '../../Analysis';
import ExaminationPage from '../../Examination';
import RadioTherapyByCesiumPage from '../../radio-therapy-by-cesium';
import RadioTherapyByCobaltPage from '../../radio-therapy-by-cobalt';
import RadioTherapyByLinacPage from '../../radio-therapy-by-linac';
import RadioTherapyByIodinePage from '../../radio-therapy-by-lodine';
import { Divider } from 'antd';
import RadialImageByCTPage from '../../radial-image-by-ct';
import RadialImageByGammaPage from '../../radial-image-by-gamma';
import RadialImageByMRIPage from '../../radial-image-by-mri';
import RadialImageByXRAYPage from '../../radial-image-by-xray';
import radialImageByCT from '../../../../../api/nuclearMedicineApi/services/RadialImageByCT';
import radialImageByGamma from '../../../../../api/nuclearMedicineApi/services/RadialImageByGamma';
import { AuthContext } from '../../../../../contexts/auth-context';
import { panelPlan } from '../../../../../constants/enums';
interface PlanFormProps extends IFormProps { }
const PlanForm: FunctionComponent<PlanFormProps> = ({
control,
disabled,
type,
data,
}) => {
const authContext = useContext(AuthContext);
const role = authContext?.userData?.roleNames?.[0];
const userRoles = authContext?.userData?.roleNames;
const sharedProps = {
control,
disabled,
};
const { caseId, planId } = useParams();
const [analysisCalled, setAnalysisCalled] = useState(false);
const [ct, setCt] = useState(false);
const [gamma, setGamma]= useState(false);
const [mri, setMri] = useState(false);
const [xray, setXray] = useState(false);
const [cesium, setCesium] = useState(false);
const [cobalt, setCobalt] = useState(false);
const [linac, setLinac] = useState(false);
const [iodin, setIodin] = useState(false);
const [examinationCalled, setExaminationCalled] = useState(false);
const { Panel } = Collapse;
const results = useQueries([
{
queryKey: ['examination', caseId, planId],
enabled: !! examinationCalled,
queryFn: () =>
examination
.examinationGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
{
queryKey: ['analysis', caseId, planId],
enabled: !! analysisCalled,
queryFn: () =>
analysis
.analysisGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
// ***************************************
{
queryKey: ['RadialImageByCT', caseId, planId],
enabled: !! ct,
queryFn: () =>
radialImageByCT
.RadialImageByCTGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
{
queryKey: ['RadialImageByMRI', caseId, planId],
enabled: !! mri,
queryFn: () =>
radialImageByMRI
.radialImageByMRIGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
{
queryKey: ['RadialImageByGamma', caseId, planId],
enabled: !!gamma,
queryFn: () =>
radialImageByGamma
.RadialImageByGammaGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
{
queryKey: ['RadialImageByXRay', caseId, planId],
enabled: !! xray,
queryFn: () =>
radialImage
.radialImageGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
// ****************************
{
queryKey: ['radioTherapyByCesium', caseId, planId],
enabled: !! cesium,
queryFn: () =>
radioTherapyByCesium
.RadioTherapyByCesiumGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
{
queryKey: ['radioTherapyByCobalt', caseId, planId],
enabled: !! cobalt,
queryFn: () =>
radioTherapyByCobalt
.RadioTherapyByCobaltGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
{
queryKey: ['radioTherapyByLinac', caseId, planId],
enabled: !! linac,
queryFn: () =>
radioTherapyByLinac
.RadioTherapyByLinacGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
{
queryKey: ['radioTherapyByIodine', caseId, planId],
enabled: !! iodin,
queryFn: () =>
radioTherapyByIodine
.RadioTherapyByIodineGetAll({
CaseId: +caseId!,
PlanId: +planId!,
})
.then((data) => data.totalCount),
},
]);
let libMax = 0;
libMax = Math.max(...results.slice(6, 11).map((item, index) => +item.data!))
if (libMax === 0)
libMax = Math.max(...results.slice(2, 6).map((item, index) => +item.data!))
// console.log("max count : " + libMax);
let indexQueryMaxCount = 0;
results?.map((row: any, index: number) => {
if (row?.data === libMax)
indexQueryMaxCount = index;
});
const genExtra = (queryKey: number) => (
<Tag>{results[queryKey].data}</Tag>
);
return (
<Row gutter={3}>
<Col className='mb-8' span={12}>
<FormElement
{...sharedProps}
type='input'
label='name'
name='name'
disabled
/>
</Col>
{(type === 'Details' || type === 'Update') && panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'Analysiss' && userAllowed) {
setAnalysisCalled(true);
return (
<Col span={24} >
<Collapse accordion defaultActiveKey={indexQueryMaxCount}>
<Panel
header={<FormattedMessage id='analysiss' />}
key='1'
extra={genExtra(1)}
style={{ backgroundColor: '#ffc90e' }}
>
<AnalysisPage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
</Collapse>
</Col>
)
}
})}
{/* *********************************** */}
{/* {
(isCt === true || isGamma === true || isMRIRadiologist === true || isXRayRadiologist === true) && */}
<Divider style={{ marginTop: '10px', marginBottom: '10px' }} />
{/* } */}
{(type === 'Details' || type === 'Update') && (
<Col span={24}>
<Collapse accordion defaultActiveKey={indexQueryMaxCount}>
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'RadialImageByCT' && userAllowed) {
setCt(true);
return (
<Panel
header={<FormattedMessage id='RadialImageByCTs' />}
key='2'
extra={genExtra(2)}
style={{ backgroundColor: '#c8bfe7' }}
>
<RadialImageByCTPage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
)
}
})}
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'RadialImageByMRI' && userAllowed) {
setMri(true);
return (<Panel
header={<FormattedMessage id='RadialImageByMRIs' />}
key='3'
extra={genExtra(3)}
style={{ backgroundColor: '#c8bfe7' }}
>
<RadialImageByMRIPage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
)
}
})}
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'RadialImageByGamma' && userAllowed) {
setGamma(true);
return (
<Panel
header={<FormattedMessage id='RadialImageByGamma' />}
key='4'
extra={genExtra(4)}
style={{ backgroundColor: '#c8bfe7' }}
>
<RadialImageByGammaPage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
)
}
})}
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'RadialImageByXRay' && userAllowed) {
setXray(true);
return (
<Panel
header={<FormattedMessage id='RadialImageByXRays' />}
key='5'
extra={genExtra(5)}
style={{ backgroundColor: '#c8bfe7' }}
>
<RadialImageByXRAYPage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
)
}
})}
{/* ********************* */}
</Collapse>
</Col>
)}
<Divider style={{ marginTop: '10px', marginBottom: '10px' }} />
{(type === 'Details' || type === 'Update') && (
<Col span={24}>
<Collapse accordion defaultActiveKey={indexQueryMaxCount}>
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'RadioTherapyByCesium' && userAllowed) {
setCesium(true);
return (
<Panel
header={
<FormattedMessage id='CesiumRadioTherapy' />
}
key='6'
extra={genExtra(6)}
>
<RadioTherapyByCesiumPage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
)
}
})}
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'RadioTherapyByCobalt' && userAllowed) {
setCobalt(true);
return (
<Panel
header={
<FormattedMessage id='CobaltRadioTherapy' />
}
key='7'
extra={genExtra(7)}
>
<RadioTherapyByCobaltPage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
)
}
})}
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'RadioTherapyByLinac' && userAllowed) {
setLinac(true);
return (
<Panel
header={
<FormattedMessage id='LinacRadioTherapy' />
}
key='8'
extra={genExtra(8)}
>
<RadioTherapyByLinacPage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
)
}
})}
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'RadioTherapyByIodine' && userAllowed) {
setIodin(true);
return (
<Panel
header={
<FormattedMessage id='IodineRadioTherapy' />
}
key='9'
extra={genExtra(9)}
>
<RadioTherapyByIodinePage
caseId={Number(caseId)}
planId={data?.id}
/>
</Panel>
)
}
})}
</Collapse>
</Col>
)}
{panelPlan.map((tab) => {
const userAllowed = tab.allowedRoles.some(e => userRoles?.includes(e));
if (tab.name == 'Examination' && userAllowed) {
setExaminationCalled(true)
return (
<>
<Divider style={{ marginTop: '10px', marginBottom: '10px' }} />
{(type === 'Details' || type === 'Update') && (
<Col span={24}>
<Collapse accordion>
<Panel
header={<FormattedMessage id='examinations' />}
key='0'
extra={genExtra(0)}
style={{ backgroundColor: '#99d9ea' }}
>
<ExaminationPage
planId={data?.id}
caseId={Number(caseId)}
/>
</Panel>
</Collapse>
</Col>
)}
</>
)
}
})}
</Row>
);
};
export default PlanForm;
You are calling setState
functions (such as setAnalysisCalled
) in the render method. In your case, IIUC, when some tab is active, it will call that function during the rendering of the component. That will cause another re-render, and so on forever.
I am not sure of the architecture of your component, but this pattern is not forbidden per se, even if, instead of analysisCalled
, maybe you don't need that additional variable, you could use the same condition as what made the tab or color, etc. display in that case?
Anyway, if you still need to call setState
in the render method, and to avoid infinite loop, you have to check the current value.
So, instead of:
setAnalysisCalled(true);
You would do:
if (!analysisCalled) setAnalysisCalled(true);
So that it will not re-run at the next re-render, when the value is already true
.
Same for all other setAbcd
called inside the render method.
Edit:
More detailed suggestion about using derivative data instead of a new boolean:
instead of:
const [examinationCalled, setExaminationCalled] = useState(false)
...
if (tab.name == 'Examination' && userAllowed) {
setExaminationCalled(true)
...
You can use more efficient code as following:
const tab = panelPlan.find(tab => tab.name === 'Examination')
const examinationEnabled = = tab.allowedRoles.some(e => userRoles?.includes(e))
Actually, you could have a map of allowed roles, such as:
const allowed = panelPlan.reduce((res, tab) => {
res[tab.name] = tab.allowedRoles.some(e => userRoles?.includes(e))
return res
}, {})
That would allow, in your queries, to check allowance like so:
// Example for Examination
enabled: allowed['Examination']
Last suggestion: wherever you have a map
to set a value in your component, it is a good idea to wrap it in a useMemo
so that it is only computed when the dependencies of that useMemo
change, leading to snappier UI.
Example with my allowed
derived state above:
const allowed = useMemo(() => panelPlan.reduce((res, tab) => {
res[tab.name] = tab.allowedRoles.some(e => userRoles?.includes(e))
return res
}, {}), [panelPlan])
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments