redux 为什么当RTK在查询中时,刷新时,isFetching保持true并且不会更新,尽管正在重新获取或已经有数据?

0vvn1miw  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(113)

我有一个奇怪的问题,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 &nbsp;<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 });

寻求帮助,谢谢!

flmtquvp

flmtquvp1#

结果是redux-persistance缓存了rtk的查询,导致isFetching状态卡住。
通过删除store.ts中的rtk redux-persistence,解决了这个问题

相关问题