mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 00:19:55 +03:00
refactor(rtk-query): add custom logic for TreeView shouldExpandNode
Other changes: * feat: added duration in QueryPreviewInfo tab * refactor: TreeView component
This commit is contained in:
parent
7a274d9025
commit
dc5045f280
|
@ -1,5 +1,7 @@
|
||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import React, { ReactNode, PureComponent } from 'react';
|
import React, { ReactNode, PureComponent } from 'react';
|
||||||
import { AnyAction } from 'redux';
|
import { Action, AnyAction } from 'redux';
|
||||||
|
import { emptyRecord, identity } from '../utils/object';
|
||||||
import { TreeView } from './TreeView';
|
import { TreeView } from './TreeView';
|
||||||
|
|
||||||
export interface QueryPreviewActionsProps {
|
export interface QueryPreviewActionsProps {
|
||||||
|
@ -7,10 +9,59 @@ export interface QueryPreviewActionsProps {
|
||||||
actionsOfQuery: AnyAction[];
|
actionsOfQuery: AnyAction[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const keySep = ' - ';
|
||||||
|
|
||||||
export class QueryPreviewActions extends PureComponent<QueryPreviewActionsProps> {
|
export class QueryPreviewActions extends PureComponent<QueryPreviewActionsProps> {
|
||||||
|
selectFormattedActions = createSelector<
|
||||||
|
AnyAction[],
|
||||||
|
AnyAction[],
|
||||||
|
Record<string, AnyAction>
|
||||||
|
>(identity, (actions) => {
|
||||||
|
const output: Record<string, AnyAction> = {};
|
||||||
|
|
||||||
|
if (actions.length === 0) {
|
||||||
|
return emptyRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0, len = actions.length; i < len; i++) {
|
||||||
|
const action = actions[i];
|
||||||
|
const key = `${i}${keySep}${(action as Action<string>)?.type ?? ''}`;
|
||||||
|
output[key] = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
});
|
||||||
|
|
||||||
|
shouldExpandNode = (
|
||||||
|
keyPath: (string | number)[],
|
||||||
|
value: unknown,
|
||||||
|
layer: number
|
||||||
|
): boolean => {
|
||||||
|
if (layer === 1) {
|
||||||
|
const len = this.props.actionsOfQuery.length;
|
||||||
|
const lastKey = keyPath[keyPath.length - 1];
|
||||||
|
|
||||||
|
if (typeof lastKey === 'string') {
|
||||||
|
const index = Number(lastKey.split(keySep)[0]);
|
||||||
|
|
||||||
|
return len - index < 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return layer <= 1;
|
||||||
|
};
|
||||||
|
|
||||||
render(): ReactNode {
|
render(): ReactNode {
|
||||||
const { isWideLayout, actionsOfQuery } = this.props;
|
const { isWideLayout, actionsOfQuery } = this.props;
|
||||||
|
|
||||||
return <TreeView data={actionsOfQuery} isWideLayout={isWideLayout} />;
|
return (
|
||||||
|
<TreeView
|
||||||
|
data={this.selectFormattedActions(actionsOfQuery)}
|
||||||
|
isWideLayout={isWideLayout}
|
||||||
|
shouldExpandNode={this.shouldExpandNode}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
import { createSelector, Selector } from '@reduxjs/toolkit';
|
import { createSelector, Selector } from '@reduxjs/toolkit';
|
||||||
|
import { QueryStatus } from '@reduxjs/toolkit/dist/query';
|
||||||
import React, { ReactNode, PureComponent } from 'react';
|
import React, { ReactNode, PureComponent } from 'react';
|
||||||
import { QueryInfo, RtkQueryState, RTKStatusFlags } from '../types';
|
import { QueryInfo, RtkQueryState, RTKStatusFlags } from '../types';
|
||||||
|
import { formatMs } from '../utils/formatters';
|
||||||
import { identity } from '../utils/object';
|
import { identity } from '../utils/object';
|
||||||
import { getQueryStatusFlags } from '../utils/rtk-query';
|
import { getQueryStatusFlags } from '../utils/rtk-query';
|
||||||
import { TreeView } from './TreeView';
|
import { TreeView } from './TreeView';
|
||||||
|
|
||||||
type ComputedQueryInfo = {
|
type QueryTimings = {
|
||||||
startedAt: string;
|
startedAt: string;
|
||||||
latestFetchAt: string;
|
latestFetchAt: string;
|
||||||
|
duration: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface FormattedQuery extends ComputedQueryInfo {
|
interface FormattedQuery {
|
||||||
queryKey: string;
|
queryKey: string;
|
||||||
reducerPath: string;
|
reducerPath: string;
|
||||||
|
timings: QueryTimings;
|
||||||
statusFlags: RTKStatusFlags;
|
statusFlags: RTKStatusFlags;
|
||||||
query: RtkQueryState;
|
query: RtkQueryState;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +26,16 @@ export interface QueryPreviewInfoProps {
|
||||||
isWideLayout: boolean;
|
isWideLayout: boolean;
|
||||||
}
|
}
|
||||||
export class QueryPreviewInfo extends PureComponent<QueryPreviewInfoProps> {
|
export class QueryPreviewInfo extends PureComponent<QueryPreviewInfoProps> {
|
||||||
|
shouldExpandNode = (
|
||||||
|
keyPath: (string | number)[],
|
||||||
|
value: unknown,
|
||||||
|
layer: number
|
||||||
|
): boolean => {
|
||||||
|
const lastKey = keyPath[keyPath.length - 1];
|
||||||
|
|
||||||
|
return layer <= 1 && lastKey !== 'query';
|
||||||
|
};
|
||||||
|
|
||||||
selectFormattedQuery: Selector<QueryInfo, FormattedQuery> = createSelector(
|
selectFormattedQuery: Selector<QueryInfo, FormattedQuery> = createSelector(
|
||||||
identity,
|
identity,
|
||||||
(queryInfo: QueryInfo): FormattedQuery => {
|
(queryInfo: QueryInfo): FormattedQuery => {
|
||||||
|
@ -37,13 +51,29 @@ export class QueryPreviewInfo extends PureComponent<QueryPreviewInfoProps> {
|
||||||
|
|
||||||
const statusFlags = getQueryStatusFlags(query);
|
const statusFlags = getQueryStatusFlags(query);
|
||||||
|
|
||||||
|
const timings = {
|
||||||
|
startedAt,
|
||||||
|
latestFetchAt,
|
||||||
|
duration: '-',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
query.fulfilledTimeStamp &&
|
||||||
|
query.startedTimeStamp &&
|
||||||
|
query.status !== QueryStatus.pending &&
|
||||||
|
query.startedTimeStamp <= query.fulfilledTimeStamp
|
||||||
|
) {
|
||||||
|
timings.duration = formatMs(
|
||||||
|
query.fulfilledTimeStamp - query.startedTimeStamp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
queryKey,
|
queryKey,
|
||||||
reducerPath,
|
reducerPath,
|
||||||
startedAt,
|
|
||||||
latestFetchAt,
|
|
||||||
statusFlags,
|
|
||||||
query: queryInfo.query,
|
query: queryInfo.query,
|
||||||
|
statusFlags,
|
||||||
|
timings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -52,6 +82,12 @@ export class QueryPreviewInfo extends PureComponent<QueryPreviewInfoProps> {
|
||||||
const { queryInfo, isWideLayout } = this.props;
|
const { queryInfo, isWideLayout } = this.props;
|
||||||
const formattedQuery = this.selectFormattedQuery(queryInfo);
|
const formattedQuery = this.selectFormattedQuery(queryInfo);
|
||||||
|
|
||||||
return <TreeView data={formattedQuery} isWideLayout={isWideLayout} />;
|
return (
|
||||||
|
<TreeView
|
||||||
|
data={formattedQuery}
|
||||||
|
isWideLayout={isWideLayout}
|
||||||
|
shouldExpandNode={this.shouldExpandNode}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import React, { ComponentProps, ReactNode } from 'react';
|
import React, { ComponentProps, ReactNode } from 'react';
|
||||||
import JSONTree from 'react-json-tree';
|
import JSONTree from 'react-json-tree';
|
||||||
import { StylingFunction } from 'react-base16-styling';
|
import { Base16Theme, StylingFunction } from 'react-base16-styling';
|
||||||
import { DATA_TYPE_KEY } from '../monitor-config';
|
import { DATA_TYPE_KEY } from '../monitor-config';
|
||||||
import {
|
import {
|
||||||
getJsonTreeTheme,
|
getJsonTreeTheme,
|
||||||
|
@ -10,28 +10,68 @@ import {
|
||||||
import { createTreeItemLabelRenderer, getItemString } from '../styles/tree';
|
import { createTreeItemLabelRenderer, getItemString } from '../styles/tree';
|
||||||
import { identity } from '../utils/object';
|
import { identity } from '../utils/object';
|
||||||
|
|
||||||
export interface TreeViewProps {
|
export interface TreeViewProps
|
||||||
|
extends Partial<
|
||||||
|
Pick<
|
||||||
|
ComponentProps<typeof JSONTree>,
|
||||||
|
'keyPath' | 'shouldExpandNode' | 'hideRoot'
|
||||||
|
>
|
||||||
|
> {
|
||||||
data: unknown;
|
data: unknown;
|
||||||
isWideLayout: boolean;
|
isWideLayout: boolean;
|
||||||
before?: ReactNode;
|
before?: ReactNode;
|
||||||
after?: ReactNode;
|
after?: ReactNode;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
keyPath?: ComponentProps<typeof JSONTree>['keyPath'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TreeView extends React.PureComponent<TreeViewProps> {
|
export class TreeView extends React.PureComponent<TreeViewProps> {
|
||||||
|
static defaultProps = {
|
||||||
|
hideRoot: true,
|
||||||
|
shouldExpandNode: (
|
||||||
|
keyPath: (string | number)[],
|
||||||
|
value: unknown,
|
||||||
|
layer: number
|
||||||
|
): boolean => {
|
||||||
|
return layer < 2;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
readonly selectLabelRenderer = createSelector<
|
readonly selectLabelRenderer = createSelector<
|
||||||
StylingFunction,
|
StylingFunction,
|
||||||
StylingFunction,
|
StylingFunction,
|
||||||
ReturnType<typeof createTreeItemLabelRenderer>
|
ReturnType<typeof createTreeItemLabelRenderer>
|
||||||
>(identity, createTreeItemLabelRenderer);
|
>(identity, createTreeItemLabelRenderer);
|
||||||
|
|
||||||
|
readonly selectGetItemString = createSelector(
|
||||||
|
[
|
||||||
|
(styling: StylingFunction, _: boolean) => styling,
|
||||||
|
(_: StylingFunction, isWideLayout: boolean) => isWideLayout,
|
||||||
|
],
|
||||||
|
(styling, isWideLayout) => (type: string, data: any) =>
|
||||||
|
getItemString(styling, type, data, DATA_TYPE_KEY, isWideLayout)
|
||||||
|
);
|
||||||
|
|
||||||
|
readonly selectTheme = createSelector<
|
||||||
|
Base16Theme,
|
||||||
|
Base16Theme,
|
||||||
|
ReturnType<typeof getJsonTreeTheme>
|
||||||
|
>(identity, getJsonTreeTheme);
|
||||||
|
|
||||||
constructor(props: TreeViewProps) {
|
constructor(props: TreeViewProps) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): ReactNode {
|
render(): ReactNode {
|
||||||
const { isWideLayout, data, before, after, children, keyPath } = this.props;
|
const {
|
||||||
|
isWideLayout,
|
||||||
|
data,
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
children,
|
||||||
|
keyPath,
|
||||||
|
shouldExpandNode,
|
||||||
|
hideRoot,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyleUtilsContext.Consumer>
|
<StyleUtilsContext.Consumer>
|
||||||
|
@ -41,20 +81,13 @@ export class TreeView extends React.PureComponent<TreeViewProps> {
|
||||||
{before}
|
{before}
|
||||||
<JSONTree
|
<JSONTree
|
||||||
keyPath={keyPath}
|
keyPath={keyPath}
|
||||||
|
shouldExpandNode={shouldExpandNode}
|
||||||
data={data}
|
data={data}
|
||||||
labelRenderer={this.selectLabelRenderer(styling)}
|
labelRenderer={this.selectLabelRenderer(styling)}
|
||||||
theme={getJsonTreeTheme(base16Theme)}
|
theme={this.selectTheme(base16Theme)}
|
||||||
invertTheme={invertTheme}
|
invertTheme={invertTheme}
|
||||||
getItemString={(type, data) =>
|
getItemString={this.selectGetItemString(styling, isWideLayout)}
|
||||||
getItemString(
|
hideRoot={hideRoot}
|
||||||
styling,
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
DATA_TYPE_KEY,
|
|
||||||
isWideLayout
|
|
||||||
)
|
|
||||||
}
|
|
||||||
hideRoot
|
|
||||||
/>
|
/>
|
||||||
{after}
|
{after}
|
||||||
{children}
|
{children}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user