mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 00:19:55 +03:00
refactor(rtk-query): reduce selector computations
Other changes: * simplify TreeView props
This commit is contained in:
parent
ad02c0ab26
commit
71b483bf02
|
@ -2,51 +2,101 @@ import React, { ReactNode } from 'react';
|
|||
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
||||
import { createTreeItemLabelRenderer } from '../styles/tree';
|
||||
import {
|
||||
QueryPreviewTabOption,
|
||||
QueryPreviewTabs,
|
||||
QueryPreviewTabProps,
|
||||
QueryInfo,
|
||||
SelectorsSource,
|
||||
TabOption,
|
||||
} from '../types';
|
||||
import { QueryPreviewHeader } from './QueryPreviewHeader';
|
||||
import { QueryPreviewInfo } from './QueryPreviewInfo';
|
||||
import { QueryPreviewApi } from './QueryPreviewApi';
|
||||
import { QueryPreviewSubscriptions } from './QueryPreviewSubscriptions';
|
||||
import { QueryPreviewTags } from './QueryPreviewTags';
|
||||
import { QueryPreviewInfo, QueryPreviewInfoProps } from './QueryPreviewInfo';
|
||||
import { QueryPreviewApi, QueryPreviewApiProps } from './QueryPreviewApi';
|
||||
import {
|
||||
QueryPreviewSubscriptions,
|
||||
QueryPreviewSubscriptionsProps,
|
||||
} from './QueryPreviewSubscriptions';
|
||||
import { QueryPreviewTags, QueryPreviewTagsProps } from './QueryPreviewTags';
|
||||
import { NoRtkQueryApi } from './NoRtkQueryApi';
|
||||
import { InspectorSelectors } from '../selectors';
|
||||
import { StylingFunction } from 'react-base16-styling';
|
||||
import { mapProps } from '../containers/mapProps';
|
||||
|
||||
export interface QueryPreviewProps
|
||||
extends Omit<QueryPreviewTabProps, 'base16Theme' | 'invertTheme'> {
|
||||
selectedTab: QueryPreviewTabs;
|
||||
hasNoApis: boolean;
|
||||
onTabChange: (tab: QueryPreviewTabs) => void;
|
||||
export interface QueryPreviewProps<S = unknown> {
|
||||
readonly selectedTab: QueryPreviewTabs;
|
||||
readonly hasNoApis: boolean;
|
||||
readonly onTabChange: (tab: QueryPreviewTabs) => void;
|
||||
readonly queryInfo: QueryInfo | null;
|
||||
readonly styling: StylingFunction;
|
||||
readonly isWideLayout: boolean;
|
||||
readonly selectorsSource: SelectorsSource<S>;
|
||||
readonly selectors: InspectorSelectors<S>;
|
||||
}
|
||||
|
||||
const tabs: ReadonlyArray<QueryPreviewTabOption> = [
|
||||
/**
|
||||
* Tab content is not rendered if there's no selected query.
|
||||
*/
|
||||
type QueryPreviewTabProps = Omit<QueryPreviewProps<unknown>, 'queryInfo'> & {
|
||||
queryInfo: QueryInfo;
|
||||
};
|
||||
|
||||
const MappedQueryPreviewTags = mapProps<
|
||||
QueryPreviewTabProps,
|
||||
QueryPreviewTagsProps
|
||||
>(({ selectors, selectorsSource, isWideLayout, queryInfo }) => ({
|
||||
queryInfo,
|
||||
tags: selectors.selectCurrentQueryTags(selectorsSource),
|
||||
isWideLayout,
|
||||
}))(QueryPreviewTags);
|
||||
|
||||
const MappedQueryPreviewInfo = mapProps<
|
||||
QueryPreviewTabProps,
|
||||
QueryPreviewInfoProps
|
||||
>(({ queryInfo, isWideLayout }) => ({ queryInfo, isWideLayout }))(
|
||||
QueryPreviewInfo
|
||||
);
|
||||
|
||||
const MappedQuerySubscriptipns = mapProps<
|
||||
QueryPreviewTabProps,
|
||||
QueryPreviewSubscriptionsProps
|
||||
>(({ selectors, selectorsSource, isWideLayout }) => ({
|
||||
isWideLayout,
|
||||
subscriptions: selectors.selectSubscriptionsOfCurrentQuery(selectorsSource),
|
||||
}))(QueryPreviewSubscriptions);
|
||||
|
||||
const MappedApiPreview = mapProps<QueryPreviewTabProps, QueryPreviewApiProps>(
|
||||
({ isWideLayout, selectors, selectorsSource }) => ({
|
||||
isWideLayout,
|
||||
apiState: selectors.selectApiOfCurrentQuery(selectorsSource),
|
||||
apiStats: selectors.selectApiStatsOfCurrentQuery(selectorsSource),
|
||||
})
|
||||
)(QueryPreviewApi);
|
||||
|
||||
const tabs: ReadonlyArray<TabOption<QueryPreviewTabs, QueryPreviewTabProps>> = [
|
||||
{
|
||||
label: 'query',
|
||||
value: QueryPreviewTabs.queryinfo,
|
||||
component: QueryPreviewInfo,
|
||||
component: MappedQueryPreviewInfo,
|
||||
},
|
||||
{
|
||||
label: 'tags',
|
||||
value: QueryPreviewTabs.queryTags,
|
||||
component: QueryPreviewTags,
|
||||
component: MappedQueryPreviewTags,
|
||||
},
|
||||
{
|
||||
label: 'subs',
|
||||
value: QueryPreviewTabs.querySubscriptions,
|
||||
component: QueryPreviewSubscriptions,
|
||||
component: MappedQuerySubscriptipns,
|
||||
},
|
||||
{
|
||||
label: 'api',
|
||||
value: QueryPreviewTabs.apiConfig,
|
||||
component: QueryPreviewApi,
|
||||
component: MappedApiPreview,
|
||||
},
|
||||
];
|
||||
|
||||
export class QueryPreview extends React.PureComponent<QueryPreviewProps> {
|
||||
export class QueryPreview<S> extends React.PureComponent<QueryPreviewProps<S>> {
|
||||
readonly labelRenderer: ReturnType<typeof createTreeItemLabelRenderer>;
|
||||
|
||||
constructor(props: QueryPreviewProps) {
|
||||
constructor(props: QueryPreviewProps<S>) {
|
||||
super(props);
|
||||
|
||||
this.labelRenderer = createTreeItemLabelRenderer(this.props.styling);
|
||||
|
@ -65,40 +115,19 @@ export class QueryPreview extends React.PureComponent<QueryPreviewProps> {
|
|||
return `${label} (${counterAsString})`;
|
||||
};
|
||||
|
||||
renderTabLabel = (tab: QueryPreviewTabOption): ReactNode => {
|
||||
const { queryInfo, tags, querySubscriptions } = this.props;
|
||||
if (queryInfo) {
|
||||
if (tab.value === QueryPreviewTabs.queryTags && tags.length > 0) {
|
||||
return this.renderLabelWithCounter(tab.label, tags.length);
|
||||
}
|
||||
renderTabLabel = (tab: TabOption<QueryPreviewTabs, unknown>): ReactNode => {
|
||||
const { selectors, selectorsSource } = this.props;
|
||||
const tabCount = selectors.selectTabCounters(selectorsSource)[tab.value];
|
||||
|
||||
if (
|
||||
tab.value === QueryPreviewTabs.querySubscriptions &&
|
||||
querySubscriptions
|
||||
) {
|
||||
const subsCount = Object.keys(querySubscriptions).length;
|
||||
|
||||
if (subsCount > 0) {
|
||||
return this.renderLabelWithCounter(tab.label, subsCount);
|
||||
}
|
||||
}
|
||||
if (tabCount > 0) {
|
||||
return this.renderLabelWithCounter(tab.label, tabCount);
|
||||
}
|
||||
|
||||
return tab.label;
|
||||
};
|
||||
|
||||
render(): ReactNode {
|
||||
const {
|
||||
queryInfo,
|
||||
isWideLayout,
|
||||
selectedTab,
|
||||
apiState,
|
||||
onTabChange,
|
||||
querySubscriptions,
|
||||
tags,
|
||||
apiStats,
|
||||
hasNoApis,
|
||||
} = this.props;
|
||||
const { queryInfo, selectedTab, onTabChange, hasNoApis } = this.props;
|
||||
|
||||
const { component: TabComponent } =
|
||||
tabs.find((tab) => tab.value === selectedTab) || tabs[0];
|
||||
|
@ -111,7 +140,9 @@ export class QueryPreview extends React.PureComponent<QueryPreviewProps> {
|
|||
<QueryPreviewHeader
|
||||
selectedTab={selectedTab}
|
||||
onTabChange={onTabChange}
|
||||
tabs={tabs}
|
||||
tabs={
|
||||
tabs as ReadonlyArray<TabOption<QueryPreviewTabs, unknown>>
|
||||
}
|
||||
renderTabLabel={this.renderTabLabel}
|
||||
/>
|
||||
{hasNoApis && <NoRtkQueryApi />}
|
||||
|
@ -123,26 +154,18 @@ export class QueryPreview extends React.PureComponent<QueryPreviewProps> {
|
|||
|
||||
return (
|
||||
<StyleUtilsContext.Consumer>
|
||||
{({ styling, base16Theme, invertTheme }) => {
|
||||
{({ styling }) => {
|
||||
return (
|
||||
<div {...styling('queryPreview')}>
|
||||
<QueryPreviewHeader
|
||||
selectedTab={selectedTab}
|
||||
onTabChange={onTabChange}
|
||||
tabs={tabs}
|
||||
tabs={
|
||||
tabs as ReadonlyArray<TabOption<QueryPreviewTabs, unknown>>
|
||||
}
|
||||
renderTabLabel={this.renderTabLabel}
|
||||
/>
|
||||
<TabComponent
|
||||
styling={styling}
|
||||
base16Theme={base16Theme}
|
||||
invertTheme={invertTheme}
|
||||
querySubscriptions={querySubscriptions}
|
||||
queryInfo={queryInfo}
|
||||
tags={tags}
|
||||
apiState={apiState}
|
||||
isWideLayout={isWideLayout}
|
||||
apiStats={apiStats}
|
||||
/>
|
||||
<TabComponent {...(this.props as QueryPreviewTabProps)} />
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import React, { ReactNode, PureComponent } from 'react';
|
||||
import { QueryPreviewTabProps } from '../types';
|
||||
import { ApiStats, RtkQueryApiState } from '../types';
|
||||
import { TreeView } from './TreeView';
|
||||
|
||||
interface TreeDisplayed {
|
||||
reducerPath: string;
|
||||
api: QueryPreviewTabProps['apiState'];
|
||||
stats: QueryPreviewTabProps['apiStats'];
|
||||
export interface QueryPreviewApiProps {
|
||||
apiStats: ApiStats | null;
|
||||
apiState: RtkQueryApiState | null;
|
||||
isWideLayout: boolean;
|
||||
}
|
||||
export class QueryPreviewApi extends PureComponent<QueryPreviewTabProps> {
|
||||
|
||||
export class QueryPreviewApi extends PureComponent<QueryPreviewApiProps> {
|
||||
selectData = createSelector(
|
||||
[
|
||||
({ apiState }: QueryPreviewTabProps) => apiState,
|
||||
({ apiStats }: QueryPreviewTabProps) => apiStats,
|
||||
({ apiState }: QueryPreviewApiProps) => apiState,
|
||||
({ apiStats }: QueryPreviewApiProps) => apiStats,
|
||||
],
|
||||
(apiState, apiStats) => ({
|
||||
reducerPath: apiState?.config?.reducerPath ?? null,
|
||||
|
@ -22,20 +23,10 @@ export class QueryPreviewApi extends PureComponent<QueryPreviewTabProps> {
|
|||
);
|
||||
|
||||
render(): ReactNode {
|
||||
const { queryInfo, isWideLayout, base16Theme, styling, invertTheme } =
|
||||
this.props;
|
||||
|
||||
if (!queryInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TreeView
|
||||
data={this.selectData(this.props)}
|
||||
isWideLayout={isWideLayout}
|
||||
base16Theme={base16Theme}
|
||||
styling={styling}
|
||||
invertTheme={invertTheme}
|
||||
isWideLayout={this.props.isWideLayout}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
||||
import { QueryPreviewTabOption, QueryPreviewTabs } from '../types';
|
||||
import { QueryPreviewTabs, TabOption } from '../types';
|
||||
import { emptyArray } from '../utils/object';
|
||||
|
||||
export interface QueryPreviewHeaderProps {
|
||||
tabs: ReadonlyArray<QueryPreviewTabOption>;
|
||||
tabs: ReadonlyArray<TabOption<QueryPreviewTabs, unknown>>;
|
||||
onTabChange: (tab: QueryPreviewTabs) => void;
|
||||
selectedTab: QueryPreviewTabs;
|
||||
renderTabLabel?: (tab: QueryPreviewTabOption) => ReactNode;
|
||||
renderTabLabel?: (tab: QueryPreviewHeaderProps['tabs'][number]) => ReactNode;
|
||||
}
|
||||
|
||||
export class QueryPreviewHeader extends React.Component<QueryPreviewHeaderProps> {
|
||||
handleTabClick = (tab: QueryPreviewTabOption): void => {
|
||||
handleTabClick = (tab: QueryPreviewHeaderProps['tabs'][number]): void => {
|
||||
if (this.props.selectedTab !== tab.value) {
|
||||
this.props.onTabChange(tab.value);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import { createSelector, Selector } from '@reduxjs/toolkit';
|
||||
import React, { ReactNode, PureComponent } from 'react';
|
||||
import {
|
||||
QueryInfo,
|
||||
QueryPreviewTabProps,
|
||||
RtkQueryState,
|
||||
RTKStatusFlags,
|
||||
} from '../types';
|
||||
import { QueryInfo, RtkQueryState, RTKStatusFlags } from '../types';
|
||||
import { identity } from '../utils/object';
|
||||
import { getQueryStatusFlags } from '../utils/rtk-query';
|
||||
import { TreeView } from './TreeView';
|
||||
|
@ -22,17 +17,14 @@ interface FormattedQuery extends ComputedQueryInfo {
|
|||
query: RtkQueryState;
|
||||
}
|
||||
|
||||
export class QueryPreviewInfo extends PureComponent<QueryPreviewTabProps> {
|
||||
selectFormattedQuery: Selector<
|
||||
QueryPreviewTabProps['queryInfo'],
|
||||
FormattedQuery | null
|
||||
> = createSelector(
|
||||
export interface QueryPreviewInfoProps {
|
||||
queryInfo: QueryInfo;
|
||||
isWideLayout: boolean;
|
||||
}
|
||||
export class QueryPreviewInfo extends PureComponent<QueryPreviewInfoProps> {
|
||||
selectFormattedQuery: Selector<QueryInfo, FormattedQuery> = createSelector(
|
||||
identity,
|
||||
(queryInfo: QueryInfo | null): FormattedQuery | null => {
|
||||
if (!queryInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
(queryInfo: QueryInfo): FormattedQuery => {
|
||||
const { query, queryKey, reducerPath } = queryInfo;
|
||||
|
||||
const startedAt = query.startedTimeStamp
|
||||
|
@ -57,23 +49,9 @@ export class QueryPreviewInfo extends PureComponent<QueryPreviewTabProps> {
|
|||
);
|
||||
|
||||
render(): ReactNode {
|
||||
const { queryInfo, isWideLayout, base16Theme, styling, invertTheme } =
|
||||
this.props;
|
||||
|
||||
const { queryInfo, isWideLayout } = this.props;
|
||||
const formattedQuery = this.selectFormattedQuery(queryInfo);
|
||||
|
||||
if (!formattedQuery) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TreeView
|
||||
data={formattedQuery}
|
||||
isWideLayout={isWideLayout}
|
||||
base16Theme={base16Theme}
|
||||
styling={styling}
|
||||
invertTheme={invertTheme}
|
||||
/>
|
||||
);
|
||||
return <TreeView data={formattedQuery} isWideLayout={isWideLayout} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +1,18 @@
|
|||
import React, { ReactNode, PureComponent } from 'react';
|
||||
import { QueryPreviewTabProps } from '../types';
|
||||
import { RtkQueryApiState } from '../types';
|
||||
import { TreeView } from './TreeView';
|
||||
|
||||
export interface QueryPreviewSubscriptionsState {
|
||||
data: { subscriptions: QueryPreviewTabProps['querySubscriptions'] };
|
||||
export interface QueryPreviewSubscriptionsProps {
|
||||
subscriptions: RtkQueryApiState['subscriptions'][keyof RtkQueryApiState['subscriptions']];
|
||||
isWideLayout: boolean;
|
||||
}
|
||||
|
||||
export class QueryPreviewSubscriptions extends PureComponent<
|
||||
QueryPreviewTabProps,
|
||||
QueryPreviewSubscriptionsState
|
||||
> {
|
||||
static getDerivedStateFromProps(
|
||||
props: QueryPreviewTabProps,
|
||||
state: QueryPreviewSubscriptionsState
|
||||
): Partial<QueryPreviewSubscriptionsState> | null {
|
||||
if (props.querySubscriptions !== state.data.subscriptions) {
|
||||
return {
|
||||
data: { subscriptions: props.querySubscriptions },
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(props: QueryPreviewTabProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
data: { subscriptions: props.querySubscriptions },
|
||||
};
|
||||
}
|
||||
|
||||
export class QueryPreviewSubscriptions extends PureComponent<QueryPreviewSubscriptionsProps> {
|
||||
render(): ReactNode {
|
||||
const {
|
||||
queryInfo,
|
||||
isWideLayout,
|
||||
base16Theme,
|
||||
styling,
|
||||
invertTheme,
|
||||
querySubscriptions,
|
||||
} = this.props;
|
||||
|
||||
if (!querySubscriptions || !queryInfo) {
|
||||
return null;
|
||||
}
|
||||
const { subscriptions } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TreeView
|
||||
data={this.state.data}
|
||||
isWideLayout={isWideLayout}
|
||||
base16Theme={base16Theme}
|
||||
styling={styling}
|
||||
invertTheme={invertTheme}
|
||||
/>
|
||||
</>
|
||||
<TreeView data={subscriptions} isWideLayout={this.props.isWideLayout} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,21 @@
|
|||
import React, { ReactNode, PureComponent } from 'react';
|
||||
import { QueryPreviewTabProps } from '../types';
|
||||
import { RtkQueryTag } from '../types';
|
||||
import { TreeView } from './TreeView';
|
||||
|
||||
interface QueryPreviewTagsState {
|
||||
data: { tags: QueryPreviewTabProps['tags'] };
|
||||
data: { tags: RtkQueryTag[] };
|
||||
}
|
||||
|
||||
export interface QueryPreviewTagsProps {
|
||||
tags: RtkQueryTag[];
|
||||
isWideLayout: boolean;
|
||||
}
|
||||
|
||||
export class QueryPreviewTags extends PureComponent<
|
||||
QueryPreviewTabProps,
|
||||
QueryPreviewTagsProps,
|
||||
QueryPreviewTagsState
|
||||
> {
|
||||
static getDerivedStateFromProps(
|
||||
{ tags }: QueryPreviewTabProps,
|
||||
state: QueryPreviewTagsState
|
||||
): QueryPreviewTagsState | null {
|
||||
if (tags !== state.data.tags) {
|
||||
return {
|
||||
data: { tags },
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(props: QueryPreviewTabProps) {
|
||||
constructor(props: QueryPreviewTagsProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
@ -32,21 +24,8 @@ export class QueryPreviewTags extends PureComponent<
|
|||
}
|
||||
|
||||
render(): ReactNode {
|
||||
const { queryInfo, isWideLayout, base16Theme, styling, invertTheme } =
|
||||
this.props;
|
||||
const { isWideLayout, tags } = this.props;
|
||||
|
||||
if (!queryInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TreeView
|
||||
data={this.state.data}
|
||||
isWideLayout={isWideLayout}
|
||||
base16Theme={base16Theme}
|
||||
styling={styling}
|
||||
invertTheme={invertTheme}
|
||||
/>
|
||||
);
|
||||
return <TreeView data={tags} isWideLayout={isWideLayout} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import React, { ComponentProps, ReactNode } from 'react';
|
||||
import JSONTree from 'react-json-tree';
|
||||
import { StylingFunction } from 'react-base16-styling';
|
||||
import { DATA_TYPE_KEY } from '../monitor-config';
|
||||
import { getJsonTreeTheme } from '../styles/createStylingFromTheme';
|
||||
import {
|
||||
getJsonTreeTheme,
|
||||
StyleUtilsContext,
|
||||
} from '../styles/createStylingFromTheme';
|
||||
import { createTreeItemLabelRenderer, getItemString } from '../styles/tree';
|
||||
import { StyleUtils } from '../types';
|
||||
import { identity } from '../utils/object';
|
||||
|
||||
export interface TreeViewProps extends StyleUtils {
|
||||
export interface TreeViewProps {
|
||||
data: unknown;
|
||||
isWideLayout: boolean;
|
||||
before?: ReactNode;
|
||||
|
@ -15,37 +20,39 @@ export interface TreeViewProps extends StyleUtils {
|
|||
}
|
||||
|
||||
export class TreeView extends React.PureComponent<TreeViewProps> {
|
||||
readonly labelRenderer: ReturnType<typeof createTreeItemLabelRenderer>;
|
||||
readonly selectLabelRenderer = createSelector<
|
||||
StylingFunction,
|
||||
StylingFunction,
|
||||
ReturnType<typeof createTreeItemLabelRenderer>
|
||||
>(identity, createTreeItemLabelRenderer);
|
||||
|
||||
constructor(props: TreeViewProps) {
|
||||
super(props);
|
||||
this.labelRenderer = createTreeItemLabelRenderer(this.props.styling);
|
||||
}
|
||||
|
||||
render(): ReactNode {
|
||||
const {
|
||||
styling,
|
||||
base16Theme,
|
||||
invertTheme,
|
||||
isWideLayout,
|
||||
data,
|
||||
before,
|
||||
after,
|
||||
children,
|
||||
keyPath,
|
||||
} = this.props;
|
||||
const { isWideLayout, data, before, after, children, keyPath } = this.props;
|
||||
|
||||
return (
|
||||
<StyleUtilsContext.Consumer>
|
||||
{({ styling, invertTheme, base16Theme }) => {
|
||||
return (
|
||||
<div {...styling('treeWrapper')}>
|
||||
{before}
|
||||
<JSONTree
|
||||
keyPath={keyPath}
|
||||
data={data}
|
||||
labelRenderer={this.labelRenderer}
|
||||
labelRenderer={this.selectLabelRenderer(styling)}
|
||||
theme={getJsonTreeTheme(base16Theme)}
|
||||
invertTheme={invertTheme}
|
||||
getItemString={(type, data) =>
|
||||
getItemString(styling, type, data, DATA_TYPE_KEY, isWideLayout)
|
||||
getItemString(
|
||||
styling,
|
||||
type,
|
||||
data,
|
||||
DATA_TYPE_KEY,
|
||||
isWideLayout
|
||||
)
|
||||
}
|
||||
hideRoot
|
||||
/>
|
||||
|
@ -53,5 +60,8 @@ export class TreeView extends React.PureComponent<TreeViewProps> {
|
|||
{children}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</StyleUtilsContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import {
|
|||
import { QueryList } from '../components/QueryList';
|
||||
import { QueryForm } from '../components/QueryForm';
|
||||
import { QueryPreview } from '../components/QueryPreview';
|
||||
import { getApiStateOf, getQuerySubscriptionsOf } from '../utils/rtk-query';
|
||||
|
||||
type ForwardedMonitorProps<S, A extends Action<unknown>> = Pick<
|
||||
LiftedState<S, A, RtkQueryMonitorState>,
|
||||
|
@ -115,26 +114,15 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends PureComponent<
|
|||
const {
|
||||
styleUtils: { styling },
|
||||
} = this.props;
|
||||
const apiStates = this.selectors.selectApiStates(selectorsSource);
|
||||
const allVisibleQueries =
|
||||
this.selectors.selectAllVisbileQueries(selectorsSource);
|
||||
|
||||
const currentQueryInfo =
|
||||
this.selectors.selectCurrentQueryInfo(selectorsSource);
|
||||
|
||||
const currentRtkApi =
|
||||
this.selectors.selectApiOfCurrentQuery(selectorsSource);
|
||||
const currentQuerySubscriptions = getQuerySubscriptionsOf(
|
||||
currentQueryInfo,
|
||||
apiStates
|
||||
);
|
||||
const apiStates = this.selectors.selectApiStates(selectorsSource);
|
||||
|
||||
const currentTags = this.selectors.selectCurrentQueryTags(selectorsSource);
|
||||
|
||||
const currentApiStats =
|
||||
this.selectors.selectApiStatsOfCurrentQuery(selectorsSource);
|
||||
|
||||
const hasNoApis = apiStates == null;
|
||||
const hasNoApi = apiStates == null;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -156,17 +144,15 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends PureComponent<
|
|||
selectedQueryKey={selectorsSource.monitorState.selectedQueryKey}
|
||||
/>
|
||||
</div>
|
||||
<QueryPreview
|
||||
<QueryPreview<S>
|
||||
selectorsSource={this.state.selectorsSource}
|
||||
selectors={this.selectors}
|
||||
queryInfo={currentQueryInfo}
|
||||
selectedTab={selectorsSource.monitorState.selectedPreviewTab}
|
||||
onTabChange={this.handleTabChange}
|
||||
styling={styling}
|
||||
tags={currentTags}
|
||||
querySubscriptions={currentQuerySubscriptions}
|
||||
apiState={currentRtkApi}
|
||||
isWideLayout={isWideLayout}
|
||||
apiStats={currentApiStats}
|
||||
hasNoApis={hasNoApis}
|
||||
hasNoApis={hasNoApi}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import React, { ComponentType, ReactNode, Component } from 'react';
|
||||
|
||||
interface Mapper<In, Out> {
|
||||
(inProps: In): Out;
|
||||
}
|
||||
|
||||
interface MapPropsOutput<In, Out> {
|
||||
(comp: ComponentType<Out>): ComponentType<In>;
|
||||
}
|
||||
|
||||
export function mapProps<In, Out>(
|
||||
mapper: Mapper<In, Out>
|
||||
): MapPropsOutput<In, Out> {
|
||||
return function mapPropsHoc(Comp) {
|
||||
class MapPropsHoc extends Component<In> {
|
||||
render(): ReactNode {
|
||||
const mappedProps = mapper(this.props);
|
||||
|
||||
return <Comp {...mappedProps} />;
|
||||
}
|
||||
|
||||
static displayName = `mapProps(${
|
||||
Comp.displayName || Comp.name || 'Component'
|
||||
})`;
|
||||
}
|
||||
|
||||
return MapPropsHoc;
|
||||
};
|
||||
}
|
|
@ -7,9 +7,11 @@ import {
|
|||
RtkQueryTag,
|
||||
SelectorsSource,
|
||||
RtkQueryProvided,
|
||||
QueryPreviewTabs,
|
||||
} from './types';
|
||||
import { Comparator, queryComparators } from './utils/comparators';
|
||||
import { FilterList, queryListFilters } from './utils/filters';
|
||||
import { emptyRecord } from './utils/object';
|
||||
import { escapeRegExpSpecialCharacter } from './utils/regexp';
|
||||
import {
|
||||
getApiStatesOf,
|
||||
|
@ -63,6 +65,14 @@ export interface InspectorSelectors<S> {
|
|||
S,
|
||||
RtkQueryApiState | null
|
||||
>;
|
||||
readonly selectTabCounters: InspectorSelector<
|
||||
S,
|
||||
Record<QueryPreviewTabs, number>
|
||||
>;
|
||||
readonly selectSubscriptionsOfCurrentQuery: InspectorSelector<
|
||||
S,
|
||||
RtkQueryApiState['subscriptions'][string]
|
||||
>;
|
||||
}
|
||||
|
||||
export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
||||
|
@ -161,6 +171,17 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
|||
return selectApiOfCurrentQuery(selectorsSource)?.provided ?? null;
|
||||
};
|
||||
|
||||
const selectSubscriptionsOfCurrentQuery = createSelector(
|
||||
[selectApiOfCurrentQuery, selectCurrentQueryInfo],
|
||||
(apiState, queryInfo) => {
|
||||
if (!queryInfo || !apiState) {
|
||||
return emptyRecord;
|
||||
}
|
||||
|
||||
return apiState.subscriptions[queryInfo.queryKey];
|
||||
}
|
||||
);
|
||||
|
||||
const selectCurrentQueryTags = createSelector(
|
||||
[selectCurrentQueryInfo, selectProvidedOfCurrentQuery],
|
||||
getQueryTagsOf
|
||||
|
@ -171,6 +192,21 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
|||
generateApiStatsOfCurrentQuery
|
||||
);
|
||||
|
||||
const selectTabCounters = (
|
||||
selectorsSource: SelectorsSource<S>
|
||||
): Record<QueryPreviewTabs, number> => {
|
||||
const subscriptions = selectSubscriptionsOfCurrentQuery(selectorsSource);
|
||||
const subsLen = Object.keys(subscriptions ?? {}).length;
|
||||
|
||||
return {
|
||||
[QueryPreviewTabs.queryTags]:
|
||||
selectCurrentQueryTags(selectorsSource).length,
|
||||
[QueryPreviewTabs.querySubscriptions]: subsLen,
|
||||
[QueryPreviewTabs.apiConfig]: 0,
|
||||
[QueryPreviewTabs.queryinfo]: 0,
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
selectQueryComparator,
|
||||
selectApiStates,
|
||||
|
@ -180,6 +216,8 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
|||
selectCurrentQueryInfo,
|
||||
selectCurrentQueryTags,
|
||||
selectApiStatsOfCurrentQuery,
|
||||
selectSubscriptionsOfCurrentQuery,
|
||||
selectApiOfCurrentQuery,
|
||||
selectTabCounters,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -132,24 +132,10 @@ export interface ApiStats {
|
|||
}>;
|
||||
}
|
||||
|
||||
export interface QueryPreviewTabProps extends StyleUtils {
|
||||
queryInfo: QueryInfo | null;
|
||||
apiState: RtkQueryApiState | null;
|
||||
querySubscriptions: RTKQuerySubscribers | null;
|
||||
isWideLayout: boolean;
|
||||
tags: RtkQueryTag[];
|
||||
apiStats: ApiStats | null;
|
||||
}
|
||||
|
||||
export interface TabOption<S, P> extends SelectOption<S> {
|
||||
component: ComponentType<P>;
|
||||
}
|
||||
|
||||
export type QueryPreviewTabOption = TabOption<
|
||||
QueryPreviewTabs,
|
||||
QueryPreviewTabProps
|
||||
>;
|
||||
|
||||
/**
|
||||
* It is Omit<RequestStatusFlags, 'status'> & { isFetching: boolean; }
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue
Block a user