我有一个奇怪的问题,RTK是陷入了isFetching状态只刷新。
如果我清除sessionStorage并加载页面,它将运行查询并加载数据。
在选项卡更改时,它甚至成功地重新获取数据。
但是,如果数据仍在获取并且我刷新,则当页面加载时,isFetching仍然停留在相同的状态并且不会更改。
我不确定原因是什么,也许是redux persist或缓存的问题?
以下是该问题的视频记录:
https://www.loom.com/share/8a3b83d4442d4cbf9b0e6dc393e6638d
下面是发生查询的组件的代码:
import React, { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet';
import { useDispatch } from 'react-redux';
import { Link, useHistory, useLocation, useParams } from 'react-router-dom';
import { DesktopOutlined, GoogleOutlined, FilePptOutlined, DownOutlined } from '@ant-design/icons';
import { Action, Role } from 'api/accessControl';
import { useGetStrategyQuery } from 'api/crudGraphQL/strategies/getStrategy';
import { config } from 'config';
import { useStrategyServiceSummary } from 'utils';
import {
fetchStrategyById,
Resource,
createKickoffDeck,
} from 'features/entitiesRedux';
import { monthlyDeliverableStrategyProjection } from 'features/entitiesRedux/models/strategy/projections';
import {
AccessControl,
Button,
Dropdown,
Menu,
notification,
PageHeader,
Skeleton,
TabPane
} from 'components';
import { useAccount } from '../../global';
import { SalesforceSyncAlert, SalesforceSyncModal } from '../components/StrategyForm/components';
import { strategyProjection } from '../strategyProjection';
import {
Approvals,
BlueprintOverviewCSVButton,
DealInfo,
EditDealInfo,
HourlyBreakdown,
MonthlyBreakdown,
OutsourceCosts,
GenerateDocument,
SalesforceContextMenu,
SuggestedOpportunitiesAlert,
Tabs,
TeamCommissions,
UpdateStatusSelect,
} from './components';
import css from './ViewStrategyPage.module.scss';
/**
* Strategies -> View strategy
*/
const ViewStrategyPage = () => {
const dispatch = useDispatch();
const { id: idRaw = '' } = useParams<{ [x: string]: any }>();
const id = Number.parseInt(idRaw);
const defaultPollingInterval = 0;
const [pollingInterval, setPollingInterval] = useState<number>(defaultPollingInterval);
const [deliverableUpdatedAt, setDeliverableUpdatedAt] = useState<Date>();
const history = useHistory();
const { account } = useAccount();
const hash = history.location.hash.replace('#', '');
/**
* RTK fetch strategy data
*/
const { data: strategy, isFetching, refetch } = useGetStrategyQuery({
id,
projection: { ...strategyProjection, ...monthlyDeliverableStrategyProjection }
}, { pollingInterval: defaultPollingInterval });
const { strategyServiceSummary, fetching: isFetchingStrategyServiceSummary, refetch: refetchStrategyServiceSummary} = useStrategyServiceSummary('Default Values', strategy?.id);
const { strategyServiceSummaryRounded, fetching: isFetchingStrategyServiceSummaryRounded, refetch: refetchStrategyServiceSummaryRounded } = useStrategyServiceSummary('Rounded Values', strategy?.id);
console.log('fetching strategy', isFetching);
console.log('fetching service summary', isFetchingStrategyServiceSummary);
console.log('fetching rounded', isFetchingStrategyServiceSummaryRounded);
console.log(strategy);
console.log(strategyServiceSummaryRounded);
const location = useLocation<{ from: string }>();
const redirect = `${config?.host}${location.pathname}` || '/';
const [currentTab, setCurrentTab] = useState<string>('deal-info');
const [openSyncModal, setOpenSyncModal] = useState<boolean>(false);
const [disableDeckGen, setDisableDeckGen] = useState<boolean>(false);
const isFinanceReviewed = (account?.roles &&
(account.roles.includes(Role.accounting) || account.roles.includes(Role.cLevel)||
account.roles.includes(Role.vpLevel)) &&
account.roles.includes(Role.blueprintsReview));
const isSuperAdminBP = (account?.roles &&
account.roles.includes(Role.superAdmin));
const isDepartmentReviewed = (account?.roles && !isFinanceReviewed && !isSuperAdminBP &&
(account.roles.includes(Role.departmentDirectors)
|| account.roles.includes(Role.blueprintsReview)));
const refetchAll = () => {
refetch();
refetchStrategyServiceSummary();
refetchStrategyServiceSummaryRounded();
};
// if Blueprint status is set to 'won', then only allow certain user roles to edit finance-related fields
const isLocked = strategy?.status === 'won' &&
![Role.superAdmin, Role.cLevel, Role.vpLevel, Role.accounting, Role.blueprintsReview].some(el => {
return (account?.roles?.length && account?.roles?.indexOf(el) > -1);
});
useEffect(() => {
/**
* Refetch once on page load to handle
* users updating an input and immediately
* reloading the page (this happens surprisingly often)
*/
refetchAll();
}, []);
useEffect(() => {
if (pollingInterval !== 0) {
if (
strategy?.deliverable?.updated_at
&& deliverableUpdatedAt !== strategy.deliverable.updated_at
&& strategy?.deliverable?.status === 'completed'
) {
setPollingInterval(0);
}
}
}, [pollingInterval, strategy?.deliverable?.updated_at]);
useEffect(() => {
if (strategy?.deliverable?.updated_at) {
setDeliverableUpdatedAt(strategy.deliverable.updated_at);
}
}, [strategy]);
const handleTabChange = (key: string) => {
history.replace(`#${key}`, {from: 'internal'});
refetchAll();
};
/**
* If it is not the users first time on the application, set back button to
* go back 1 entry in the history stack
* location.state is undefined if user did not navigate from inside of nova
* (Ex. using an external link)
*/
const handleBackButton = (): void => {
if (location?.state?.from === 'internal') {
history.go(-1);
} else {
history.push('/blueprints');
}
};
useEffect(() => {
hash ? setCurrentTab(hash) : setCurrentTab('deal-info');
}, [hash]);
useEffect(() => {
if(hash === 'approvals' && strategy?.status !== 'won' ) {
history.replace(`${strategy?.id}`);
setCurrentTab('deal-info');}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [strategy?.id]);
if ((isFetching && !strategy) || !strategy) {
return <Skeleton />;
}
/** This is used to control access of the Team Commissions and Approvals tab */
const hasTabAccess = () => {
// check if current user is the Account Director aka executive sponsor
const isExecutiveSponsor = account?.id === strategy?.executive_sponsor?.id;
const isAuthor = account?.id === strategy?.author?.id;
// user roles allowed to view Team Commissions
const permittedRoles = [
Role.superAdmin,
Role.accounting,
Role.departmentDirectors,
Role.blueprintsReview,
Role.vpLevel,
Role.cLevel,
Role.accountManagers
];
return (permittedRoles.some(el => {
return (account?.roles?.length && account?.roles?.indexOf(el) > -1);
}) || isExecutiveSponsor || isAuthor);
};
const generateKickoffDeck = async () => {
setDisableDeckGen(true);
try {
const createKickoffAction: any = await dispatch(createKickoffDeck({ strategy_id: id }));
if (createKickoffDeck.fulfilled.match(createKickoffAction)) {
notification.success({ message: 'Created Kickoff Deck' });
dispatch(fetchStrategyById({ id }));
}
if (createKickoffDeck.rejected.match(createKickoffAction)) {
const needsReauth = createKickoffAction?.error?.message?.includes('Google Account is missing');
console.log('Error creating kickoff deck');
notification.error({
message: `An error occurred while creating Kickoff Deck${needsReauth ? '. Please reauthenticate your Google account.' : ''}`
});
}
} catch (error) {
console.error('Error creating kickoff deck', error);
}
setDisableDeckGen(false);
};
/**
* Get's link to parent blueprint
* that this blueprint was duplicated from
*
* @returns blueprint html link
*/
const getDuplicatedBlueprintLink = () => {
return strategy?.parent?.id ?
<div>
Created from blueprint{' '}
<Link to={`/blueprints/${strategy?.parent?.id}`}>{strategy?.name}</Link>
</div> : <></>;
};
const kickoffDeckDisabled = !account?.accounts?.filter((account: { type: string; }) => account?.type === 'google')?.length;
return (
<div className={css.root}>
{/* <NovaSalesforceDisconnectAlert connected={salesforceConnected} /> */}
<Helmet>
<title>{strategy?.client?.name ? `${strategy?.client?.name} Blueprint` : 'Blueprint'}</title>
</Helmet>
<PageHeader
title={strategy?.client?.name}
subTitle={strategy?.name}
onBack={handleBackButton}
extra={[
<AccessControl
key="strategy-proposal"
action={[Action.update]}
resource={Resource.strategy}
showWarning={false}
>
<>
<UpdateStatusSelect strategy={strategy} />
<GenerateDocument
strategy={strategy}
account={account}
currentTab={currentTab}
setPollingInterval={setPollingInterval}
/>
<Dropdown overlay={
<Menu>
<Menu.Item key='kickoff-1' disabled={!strategy?.presentation?.deck_url}>
{
strategy?.presentation?.deck_url ?
<Button type='text' header icon={<DesktopOutlined />} disabled={kickoffDeckDisabled}>
<a href={strategy?.presentation?.deck_url} target='_blank' rel='noopener noreferrer'>
View Kickoff Deck
</a>
</Button> :
<Button onClick={generateKickoffDeck} disabled={disableDeckGen || kickoffDeckDisabled} type='text' header icon={<FilePptOutlined />}>
Generate Kickoff Deck
</Button>
}
</Menu.Item>
<Menu.Item key='kickoff-2'>
<Button type='text' header icon={<GoogleOutlined />}>
<a
href={`/services/google?redirect=${redirect}`}
rel='noopener noreferrer'
>
Reauthorize Google Account
</a>
</Button>
</Menu.Item>
</Menu>
}>
<Button type='text' header>
Kickoff Deck <DownOutlined style={{ fontSize: '12px' }} />
</Button>
</Dropdown>
{strategy?.salesforce_opportunity_id ?
<AccessControl
action={Action.update}
resource={Resource.strategy}
showWarning={false}
>
<SalesforceContextMenu
strategy={strategy}
refetch={refetch}
setOpenSyncModal={setOpenSyncModal}
/>
</AccessControl>
: <></>
}
</>
</AccessControl>,
]}
/>
{/* {Disabled until we figure out the use case} */}
{/* {strategy?.client?.salesforce_client_id && !strategy?.salesforce_opportunity_id ?
<SuggestedOpportunitiesAlert
strategyId={strategy?.id}
client={strategy?.client}
/>
: <></>
} */}
<Tabs
loading={isFetching}
onTabChange={handleTabChange}
currentTab={currentTab}
grade={strategy?.grade}
animated={false}
activeKey={currentTab}
extra={currentTab === 'deal-info' ? <BlueprintOverviewCSVButton strategy={strategy} loading={isFetching} /> : <></>}
>
<TabPane key="deal-info" tab="Blueprint Overview">
{isFetching || isFetchingStrategyServiceSummary || isFetchingStrategyServiceSummaryRounded ? <Skeleton active/> : (
<DealInfo
reviewerType={isFinanceReviewed || isSuperAdminBP ? 'Finance' : 'Department'}
strategyServiceSummary={strategyServiceSummary}
strategyServiceSummaryRounded={strategyServiceSummaryRounded}
strategy={strategy}
tabChange={hash}
account={account}
/>)}
</TabPane>
<TabPane key="monthly-breakdown" tab="Monthly Retainer & Profit">
<MonthlyBreakdown isLocked={isLocked} />
</TabPane>
<TabPane key="hourly-breakdown" tab="Services (Hours)">
<HourlyBreakdown isLocked={isLocked} />
</TabPane>
<TabPane key="outsource-costs" tab="Outsource Costs">
<OutsourceCosts isLocked={isLocked} />
</TabPane>
{hasTabAccess() ?
<TabPane key="team-commissions" tab="Team Commissions">
<TeamCommissions
reviewerType={isDepartmentReviewed || isSuperAdminBP ? 'Department' : 'Finance'}
account={account}
strategy={strategy}
isLoading={isFetching}
/>
</TabPane>
: <></>
}
{hasTabAccess()
&& (strategy?.status === 'won'
// Will add back if deny reason is to be shown on approvals page
// || (strategy.reviewer_status === 'denied'
// || strategy.departments.find((department: { reviewer_status: string; }) => department.reviewer_status === 'denied'))
)?
<TabPane key="approvals" tab="Approvals">
<Approvals strategy={strategy} />
</TabPane>
: <></>}
<TabPane key="edit-deal-info" tab="Edit Blueprint">
<EditDealInfo
openSyncModal={openSyncModal}
setOpenSyncModal={setOpenSyncModal}
/>
</TabPane>
</Tabs>
{
currentTab !== 'edit-deal-info' && strategy?.sync_to_salesforce && strategy?.salesforce_is_primary ?
<div className={css.stickyFooter}>
<div className={css.stickyFooter_children}>
<SalesforceSyncAlert sync={strategy?.salesforce_is_primary && strategy?.sync_to_salesforce} setOpenSyncModal={setOpenSyncModal} />
</div>
</div> :
<></>
}
<SalesforceSyncModal
id={strategy?.id}
loading={isFetching}
open={openSyncModal}
setOpenSyncModal={setOpenSyncModal}
sync={strategy?.sync_to_salesforce}
salesforceOpportunityId={strategy?.salesforce_opportunity_id}
salesforceIsPrimary={strategy?.salesforce_is_primary}
refetch={refetch}
/>
{strategy?.audit?.id ?
<div className={css.appraisalAlert}>
<>
Created from appraisal{' '}
<Link to={`/appraisals/${strategy?.audit?.id}`}>{strategy?.audit?.name}</Link>
</>
</div> : <></>
}
{
getDuplicatedBlueprintLink()
}
</div>
);
};
const Title = () => {
const { id } = useParams<{ [x: string]: any }>();
const { data: strategy, isFetching } = useGetStrategyQuery({
id: Number.parseInt(id),
projection: strategyProjection
});
if (isFetching || !strategy) {
return <Skeleton title={{ width: 200 }} paragraph={false} active />;
}
return <>{strategy?.name}</>;
};
Title.displayName = 'ViewStrategyPageTitle';
export default Object.assign(ViewStrategyPage, { Title });
寻求帮助,谢谢!
1条答案
按热度按时间flmtquvp1#
结果是redux-persistance缓存了rtk的查询,导致isFetching状态卡住。
通过删除store.ts中的rtk redux-persistence,解决了这个问题