mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-09-25 13:36:42 +03:00
make updating states simple and code clean up
This commit is contained in:
parent
193141bf52
commit
faf719c1fd
|
@ -1,223 +1,45 @@
|
||||||
import { AnyAction, Dispatch } from 'redux';
|
|
||||||
import React from 'react';
|
|
||||||
import { Button, Toolbar } from '@redux-devtools/ui';
|
import { Button, Toolbar } from '@redux-devtools/ui';
|
||||||
import { RtkResourceInfo } from '../types';
|
import React from 'react';
|
||||||
|
import { AnyAction, Dispatch } from 'redux';
|
||||||
|
|
||||||
interface RTKActionButtonsProps {
|
interface RTKActionButtonsProps {
|
||||||
dispatch?: Dispatch<AnyAction>;
|
dispatch?: Dispatch<AnyAction>;
|
||||||
liftedDispatch?: Dispatch<AnyAction>;
|
liftedDispatch?: Dispatch<AnyAction>;
|
||||||
resInfo?: RtkResourceInfo | null;
|
data: Record<string, AnyAction>;
|
||||||
actionsOfQuery?: AnyAction[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ACTION_TYPES = {
|
const ACTION_TYPES = {
|
||||||
REFRESH: 'REFRESH',
|
REFRESH: 'REFRESH',
|
||||||
FULFILLED: 'FULFILLED',
|
FULFILLED: 'FULFILLED',
|
||||||
TRIGGER_LOADING: 'TRIGGER_LOADING',
|
TRIGGER_FETCHING: 'TRIGGER_FETCHING',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const createFulfilledMeta = (
|
const REFRESH_TIMEOUT_MS = 1000;
|
||||||
requestStatus: string,
|
|
||||||
requestId: string,
|
|
||||||
baseEndpoint: string,
|
|
||||||
parsedArgs: any,
|
|
||||||
queryKey: string,
|
|
||||||
) => ({
|
|
||||||
arg: {
|
|
||||||
type: 'query',
|
|
||||||
subscribe: true,
|
|
||||||
forceRefetch: true,
|
|
||||||
subscriptionOptions: {
|
|
||||||
pollingInterval: 0,
|
|
||||||
skipPollingIfUnfocused: false,
|
|
||||||
},
|
|
||||||
endpointName: baseEndpoint,
|
|
||||||
originalArgs: parsedArgs || {},
|
|
||||||
queryCacheKey: queryKey,
|
|
||||||
},
|
|
||||||
requestId,
|
|
||||||
requestStatus,
|
|
||||||
fulfilledTimeStamp: Date.now(),
|
|
||||||
baseQueryMeta: {
|
|
||||||
request: {},
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
RTK_autoBatch: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const createPendingMeta = (
|
|
||||||
requestStatus: string,
|
|
||||||
requestId: string,
|
|
||||||
baseEndpoint: string,
|
|
||||||
parsedArgs: any,
|
|
||||||
queryKey: string,
|
|
||||||
) => ({
|
|
||||||
arg: {
|
|
||||||
type: 'query',
|
|
||||||
subscribe: true,
|
|
||||||
forceRefetch: true,
|
|
||||||
subscriptionOptions: {
|
|
||||||
pollingInterval: 0,
|
|
||||||
skipPollingIfUnfocused: false,
|
|
||||||
},
|
|
||||||
endpointName: baseEndpoint,
|
|
||||||
originalArgs: parsedArgs || {},
|
|
||||||
queryCacheKey: queryKey,
|
|
||||||
},
|
|
||||||
requestId,
|
|
||||||
requestStatus,
|
|
||||||
baseQueryMeta: {
|
|
||||||
request: {},
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
RTK_autoBatch: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createRtkQueryActions = (
|
|
||||||
reducerPath: string,
|
|
||||||
queryKey: string,
|
|
||||||
endpointName?: string,
|
|
||||||
currentData?: any,
|
|
||||||
originalArgs?: any,
|
|
||||||
) => {
|
|
||||||
const baseEndpoint = endpointName || queryKey.split('(')[0];
|
|
||||||
let parsedArgs = originalArgs;
|
|
||||||
if (!parsedArgs && queryKey.includes('(')) {
|
|
||||||
const argsString = queryKey.split('(')[1]?.split(')')[0];
|
|
||||||
if (
|
|
||||||
argsString === undefined ||
|
|
||||||
argsString === '' ||
|
|
||||||
argsString === 'undefined'
|
|
||||||
) {
|
|
||||||
parsedArgs = undefined;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
parsedArgs = JSON.parse(argsString);
|
|
||||||
} catch (e) {
|
|
||||||
parsedArgs = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const actualPayload = currentData || {
|
|
||||||
message: `No data available for ${baseEndpoint}`,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
endpointName: baseEndpoint,
|
|
||||||
queryKey: queryKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
refresh: {
|
|
||||||
type: `${reducerPath}/executeQuery/pending`,
|
|
||||||
payload: undefined,
|
|
||||||
meta: createPendingMeta(
|
|
||||||
'pending',
|
|
||||||
`refresh-${Date.now()}`,
|
|
||||||
baseEndpoint,
|
|
||||||
parsedArgs,
|
|
||||||
queryKey,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
fulfilled: {
|
|
||||||
type: `${reducerPath}/executeQuery/fulfilled`,
|
|
||||||
payload: actualPayload,
|
|
||||||
meta: createFulfilledMeta(
|
|
||||||
'fulfilled',
|
|
||||||
`fulfilled-${Date.now()}`,
|
|
||||||
baseEndpoint,
|
|
||||||
parsedArgs,
|
|
||||||
queryKey,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
triggerLoading: {
|
|
||||||
type: `${reducerPath}/executeQuery/pending`,
|
|
||||||
payload: undefined,
|
|
||||||
meta: createPendingMeta(
|
|
||||||
'pending',
|
|
||||||
`loading-${Date.now()}`,
|
|
||||||
baseEndpoint,
|
|
||||||
parsedArgs,
|
|
||||||
queryKey,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RTKActionButtons: React.FC<RTKActionButtonsProps> = ({
|
export const RTKActionButtons: React.FC<RTKActionButtonsProps> = ({
|
||||||
dispatch,
|
dispatch,
|
||||||
liftedDispatch,
|
liftedDispatch,
|
||||||
resInfo,
|
data,
|
||||||
actionsOfQuery,
|
|
||||||
}) => {
|
}) => {
|
||||||
// Extract current data from query actions if available
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
let currentData: any;
|
const pending = Object.values(data).filter(
|
||||||
if (actionsOfQuery && actionsOfQuery.length > 0) {
|
(action) => action.meta?.requestStatus === 'pending',
|
||||||
// Look for the most recent fulfilled action to get current data
|
|
||||||
const fulfilledActions = actionsOfQuery.filter(
|
|
||||||
(action) => action.type?.includes('/fulfilled') && action.payload,
|
|
||||||
);
|
|
||||||
if (fulfilledActions.length > 0) {
|
|
||||||
currentData = fulfilledActions[fulfilledActions.length - 1].payload;
|
|
||||||
console.log('📊 Found current data from actions:', currentData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!resInfo) {
|
|
||||||
return (
|
|
||||||
<div css={{ padding: '8px 12px', fontSize: '12px', color: 'orange' }}>
|
|
||||||
No query selected
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let originalArgs;
|
|
||||||
let lastPendingAction;
|
|
||||||
|
|
||||||
if (actionsOfQuery && actionsOfQuery.length > 0) {
|
|
||||||
const queryAction = actionsOfQuery.find(
|
|
||||||
(action) =>
|
|
||||||
action.type?.includes('/executeQuery/') ||
|
|
||||||
action.type?.includes('/pending') ||
|
|
||||||
action.type?.includes('/fulfilled'),
|
|
||||||
);
|
|
||||||
if (queryAction?.meta?.arg?.originalArgs) {
|
|
||||||
originalArgs = queryAction.meta.arg.originalArgs;
|
|
||||||
}
|
|
||||||
const pendingActions = actionsOfQuery.filter(
|
|
||||||
(action) => action.type?.includes('/pending') && action.meta?.requestId,
|
|
||||||
);
|
|
||||||
if (pendingActions.length > 0) {
|
|
||||||
lastPendingAction = pendingActions[pendingActions.length - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const actions = createRtkQueryActions(
|
|
||||||
resInfo.reducerPath,
|
|
||||||
resInfo.queryKey,
|
|
||||||
resInfo.queryKey.split('(')[0],
|
|
||||||
currentData,
|
|
||||||
originalArgs,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (lastPendingAction) {
|
const fulfilled = Object.values(data).filter(
|
||||||
actions.fulfilled.meta.requestId = lastPendingAction.meta.requestId;
|
(action) => action.meta?.requestStatus === 'fulfilled',
|
||||||
|
);
|
||||||
|
|
||||||
actions.fulfilled.meta.arg.queryCacheKey =
|
// Get the most recent actions
|
||||||
lastPendingAction.meta.arg.queryCacheKey;
|
const recentPending = pending[pending.length - 1];
|
||||||
|
const recentFulfilled = fulfilled[fulfilled.length - 1];
|
||||||
actions.triggerLoading.meta.requestId = lastPendingAction.meta.requestId;
|
|
||||||
|
|
||||||
actions.triggerLoading.meta.arg.queryCacheKey =
|
|
||||||
lastPendingAction.meta.arg.queryCacheKey;
|
|
||||||
|
|
||||||
actions.refresh.meta.requestId = lastPendingAction.meta.requestId;
|
|
||||||
|
|
||||||
actions.refresh.meta.arg.queryCacheKey =
|
|
||||||
lastPendingAction.meta.arg.queryCacheKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAction = (
|
const handleAction = (
|
||||||
actionType: keyof typeof ACTION_TYPES,
|
actionType: keyof typeof ACTION_TYPES,
|
||||||
action: AnyAction,
|
action: AnyAction,
|
||||||
|
fulfilledAction?: AnyAction,
|
||||||
) => {
|
) => {
|
||||||
|
setIsLoading(true);
|
||||||
if (liftedDispatch) {
|
if (liftedDispatch) {
|
||||||
try {
|
try {
|
||||||
const performAction = {
|
const performAction = {
|
||||||
|
@ -227,37 +49,46 @@ export const RTKActionButtons: React.FC<RTKActionButtonsProps> = ({
|
||||||
};
|
};
|
||||||
liftedDispatch(performAction);
|
liftedDispatch(performAction);
|
||||||
|
|
||||||
if (actionType === ACTION_TYPES.REFRESH && currentData) {
|
// For refresh actions, automatically dispatch fulfilled action after delay
|
||||||
|
if (actionType === ACTION_TYPES.REFRESH && fulfilledAction) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const resolveAction = {
|
const resolveAction = {
|
||||||
type: 'PERFORM_ACTION' as const,
|
type: 'PERFORM_ACTION' as const,
|
||||||
action: actions.fulfilled,
|
action: fulfilledAction,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
};
|
};
|
||||||
liftedDispatch(resolveAction);
|
liftedDispatch(resolveAction);
|
||||||
}, 1000);
|
setIsLoading(false);
|
||||||
|
}, REFRESH_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
console.error('Error in liftedDispatch:', e);
|
console.error('Error in liftedDispatch:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
try {
|
try {
|
||||||
dispatch(action);
|
dispatch(action);
|
||||||
if (actionType === ACTION_TYPES.REFRESH && currentData) {
|
|
||||||
|
// For refresh actions, automatically dispatch fulfilled action after delay
|
||||||
|
if (actionType === ACTION_TYPES.REFRESH && fulfilledAction) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dispatch(actions.fulfilled);
|
dispatch(fulfilledAction);
|
||||||
}, 1000);
|
setIsLoading(false);
|
||||||
|
}, REFRESH_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
console.error('Error in dispatch:', e);
|
console.error('Error in dispatch:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Toolbar borderPosition="bottom" style={{ alignItems: 'center' }}>
|
<Toolbar
|
||||||
|
borderPosition="bottom"
|
||||||
|
style={{ alignItems: 'center' }}
|
||||||
|
key={JSON.stringify(data)}
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
css={(theme) => ({
|
css={(theme) => ({
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
|
@ -268,26 +99,33 @@ export const RTKActionButtons: React.FC<RTKActionButtonsProps> = ({
|
||||||
>
|
>
|
||||||
RTK Query Actions:
|
RTK Query Actions:
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
tooltipPosition="top-right"
|
mark={isLoading && 'base0D'}
|
||||||
onClick={() => handleAction(ACTION_TYPES.REFRESH, actions.refresh)}
|
onClick={() =>
|
||||||
title="Force refetch"
|
handleAction(ACTION_TYPES.REFRESH, recentPending, recentFulfilled)
|
||||||
|
}
|
||||||
|
disabled={isLoading || !recentPending || !recentFulfilled}
|
||||||
>
|
>
|
||||||
Refresh
|
Refresh
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
tooltipPosition="top-right"
|
mark={isLoading && 'base0D'}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleAction(ACTION_TYPES.TRIGGER_LOADING, actions.triggerLoading)
|
handleAction(ACTION_TYPES.TRIGGER_FETCHING, recentPending)
|
||||||
}
|
}
|
||||||
title="Simulate loading state"
|
disabled={isLoading || !recentPending || !recentFulfilled}
|
||||||
>
|
>
|
||||||
Loading
|
Fetching
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
tooltipPosition="top-right"
|
onClick={() => {
|
||||||
onClick={() => handleAction(ACTION_TYPES.FULFILLED, actions.fulfilled)}
|
handleAction(ACTION_TYPES.FULFILLED, recentFulfilled);
|
||||||
title="Simulate successful response"
|
setIsLoading(false);
|
||||||
|
}}
|
||||||
|
disabled={!recentFulfilled}
|
||||||
>
|
>
|
||||||
Fulfill
|
Fulfill
|
||||||
</Button>
|
</Button>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user