fix ref type errors

This commit is contained in:
Ashley Harrison
2025-11-27 15:51:01 +00:00
parent 141ed7bdbf
commit cb90eddf84
52 changed files with 90 additions and 66 deletions
@@ -22,7 +22,7 @@ import { getBarColorByDiff, getBarColorByPackage, getBarColorByValue } from './c
import { CollapseConfig, CollapsedMap, FlameGraphDataContainer, LevelItem } from './dataTransform';
type RenderOptions = {
canvasRef: RefObject<HTMLCanvasElement>;
canvasRef: RefObject<HTMLCanvasElement | null>;
data: FlameGraphDataContainer;
root: LevelItem;
direction: 'children' | 'parents';
@@ -373,7 +373,7 @@ function useColorFunction(
);
}
function useSetupCanvas(canvasRef: RefObject<HTMLCanvasElement>, wrapperWidth: number, numberOfLevels: number) {
function useSetupCanvas(canvasRef: RefObject<HTMLCanvasElement | null>, wrapperWidth: number, numberOfLevels: number) {
const [ctx, setCtx] = useState<CanvasRenderingContext2D>();
useEffect(() => {
@@ -18,7 +18,7 @@ interface ComboboxListProps<T extends string | number> {
options: Array<ComboboxOption<T>>;
highlightedIndex: number | null;
selectedItems?: Array<ComboboxOption<T>>;
scrollRef: React.RefObject<HTMLDivElement>;
scrollRef: React.RefObject<HTMLDivElement | null>;
getItemProps: UseComboboxPropGetters<ComboboxOption<T>>['getItemProps'];
enableAllOption?: boolean;
isMultiSelect?: boolean;
@@ -10,7 +10,7 @@ import { useStyles2 } from '../../themes/ThemeContext';
import { List } from '../List/List';
interface DataLinkSuggestionsProps {
activeRef?: React.RefObject<HTMLDivElement>;
activeRef?: React.RefObject<HTMLDivElement | null>;
suggestions: VariableSuggestion[];
activeIndex: number;
onSuggestionSelect: (suggestion: VariableSuggestion) => void;
@@ -7,7 +7,7 @@ const CAUGHT_KEYS = ['ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter', 'Tab'];
/** @internal */
export interface UseListFocusProps {
localRef: RefObject<HTMLUListElement>;
localRef: RefObject<HTMLUListElement | null>;
options: TimeOption[];
}
@@ -168,7 +168,9 @@ export class Gauge extends PureComponent<Props> {
const gaugeElement = (
<div
style={{ height: `${autoProps.gaugeHeight}px`, width: gaugeWidth }}
ref={(element) => (this.canvasElement = element)}
ref={(element) => {
this.canvasElement = element;
}}
/>
);
@@ -6,7 +6,7 @@ const UNFOCUSED = -1;
/** @internal */
export interface UseMenuFocusProps {
localRef: RefObject<HTMLDivElement>;
localRef: RefObject<HTMLDivElement | null>;
isMenuOpen?: boolean;
close?: () => void;
onOpen?: (focusOnItem: (itemId: number) => void) => void;
@@ -211,7 +211,9 @@ export class UnThemedQueryField extends PureComponent<QueryFieldProps, QueryFiel
<div className={cx(wrapperClassName, styles.wrapper)}>
<div className="slate-query-field" data-testid={selectors.components.QueryField.container}>
<Editor
ref={(editor) => (this.editor = editor!)}
ref={(editor) => {
this.editor = editor;
}}
schema={SCHEMA}
autoCorrect={false}
readOnly={this.props.disabled}
@@ -20,7 +20,7 @@ export interface TableCellTooltipProps {
field: Field;
getActions: (field: Field, rowIdx: number) => ActionModel[];
getTextColorForBackground: (bgColor: string) => string;
gridRef: RefObject<DataGridHandle>;
gridRef: RefObject<DataGridHandle | null>;
height: number;
placement?: TableCellTooltipPlacement;
renderer: TableCellRenderer;
@@ -463,7 +463,7 @@ export function useColumnResize(
return dataGridResizeHandler;
}
export function useScrollbarWidth(ref: RefObject<DataGridHandle>, height: number) {
export function useScrollbarWidth(ref: RefObject<DataGridHandle | null>, height: number) {
const [scrollbarWidth, setScrollbarWidth] = useState(0);
useLayoutEffect(() => {
@@ -49,7 +49,7 @@ interface RowsListProps {
listHeight: number;
width: number;
cellHeight?: TableCellHeight;
listRef: React.RefObject<VariableSizeList>;
listRef: React.RefObject<VariableSizeList | null>;
tableState: TableState;
tableStyles: TableStyles;
nestedDataField?: Field;
@@ -135,7 +135,7 @@ export const Table = memo((props: Props) => {
// `useTableStateReducer`, which is needed to construct options for `useTable` (the hook that returns
// `toggleAllRowsExpanded`), and if we used a variable, that variable would be undefined at the time
// we initialize `useTableStateReducer`.
const toggleAllRowsExpandedRef = useRef<(value?: boolean) => void>();
const toggleAllRowsExpandedRef = useRef<(value?: boolean) => void>(null);
// Internal react table state reducer
const stateReducer = useTableStateReducer({
@@ -43,7 +43,7 @@ export function useFixScrollbarContainer(
*/
export function useResetVariableListSizeCache(
extendedState: GrafanaTableState,
listRef: React.RefObject<VariableSizeList>,
listRef: React.RefObject<VariableSizeList | null>,
data: DataFrame,
hasUniqueId: boolean
) {
@@ -19,7 +19,7 @@ interface EventsCanvasProps {
}
export function EventsCanvas({ id, events, renderEventMarker, mapEventToXYCoords, config }: EventsCanvasProps) {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot>(null);
// render token required to re-render annotation markers. Rendering lines happens in uPlot and the props do not change
// so we need to force the re-render when the draw hook was performed by uPlot
const [renderToken, setRenderToken] = useState(0);
@@ -140,7 +140,7 @@ export const TooltipPlugin2 = ({
const [{ plot, isHovering, isPinned, contents, style, dismiss }, setState] = useReducer(mergeState, null, initState);
const sizeRef = useRef<TooltipContainerSize>();
const sizeRef = useRef<TooltipContainerSize>(null);
const styles = useStyles2(getStyles, maxWidth);
const renderRef = useRef(render);
@@ -384,7 +384,9 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
<div className="graph-panel" aria-label={ariaLabel}>
<div
className="graph-panel__chart"
ref={(e) => (this.element = e)}
ref={(e) => {
this.element = e;
}}
style={{ height, width }}
onMouseLeave={() => {
this.setState({ isTooltipVisible: false });
@@ -96,7 +96,7 @@ export interface GraphNGState {
export class GraphNG extends Component<GraphNGProps, GraphNGState> {
static contextType = PanelContextRoot;
panelContext: PanelContext = {} as PanelContext;
private plotInstance: React.RefObject<uPlot>;
private plotInstance: React.RefObject<uPlot | null>;
private subscription = new Subscription();
@@ -53,7 +53,7 @@ export const TooltipPlugin = ({
renderTooltip,
...otherProps
}: TooltipPluginProps) => {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot>(null);
const theme = useTheme2();
const [focusedSeriesIdx, setFocusedSeriesIdx] = useState<number | null>(null);
const [focusedPointIdx, setFocusedPointIdx] = useState<number | null>(null);
@@ -19,7 +19,7 @@ export function useDelayedSwitch(value: boolean, options: DelayOptions = {}): bo
const { duration = 250, delay = 250 } = options;
const [delayedValue, setDelayedValue] = useState(value);
const onStartTime = useRef<Date | undefined>();
const onStartTime = useRef<Date | undefined>(undefined);
useEffect(() => {
let timeout: ReturnType<typeof setTimeout> | undefined;
@@ -109,7 +109,7 @@ const defaultMatchers = {
* "Time as X" core component, expects ascending x
*/
export class GraphNG extends Component<GraphNGProps, GraphNGState> {
private plotInstance: React.RefObject<uPlot>;
private plotInstance: React.RefObject<uPlot | null>;
constructor(props: GraphNGProps) {
super(props);
@@ -84,8 +84,8 @@ export function useAsync<Result, Args extends unknown[] = unknown[]>(
error: undefined,
result: initialValue,
});
const promiseRef = useRef<Promise<Result>>();
const argsRef = useRef<Args>();
const promiseRef = useRef<Promise<Result>>(undefined);
const argsRef = useRef<Args>(undefined);
const methods = useSyncedRef({
execute(...params: Args) {
@@ -155,8 +155,8 @@ export function useRuleGroupConsistencyCheck() {
const { isGroupInSync } = useRuleGroupIsInSync();
const [groupConsistent, setGroupConsistent] = useState<boolean | undefined>();
const apiCheckInterval = useRef<ReturnType<typeof setTimeout> | undefined>();
const timeoutInterval = useRef<ReturnType<typeof setTimeout> | undefined>();
const apiCheckInterval = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const timeoutInterval = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
useEffect(() => {
return () => {
@@ -245,8 +245,8 @@ export function useRuleGroupConsistencyCheck() {
export function usePrometheusConsistencyCheck() {
const { matchingPromRuleExists } = useMatchingPromRuleExists();
const removalConsistencyInterval = useRef<number | undefined>();
const creationConsistencyInterval = useRef<number | undefined>();
const removalConsistencyInterval = useRef<number | undefined>(undefined);
const creationConsistencyInterval = useRef<number | undefined>(undefined);
useEffect(() => {
return () => {
@@ -141,7 +141,7 @@ function useScopesRow(onApply: () => void) {
function useGlobalScopesSearch(searchQuery: string, parentId?: string | null) {
const { selectScope, searchAllNodes, getScopeNodes } = useScopeServicesState();
const [actions, setActions] = useState<CommandPaletteAction[] | undefined>(undefined);
const searchQueryRef = useRef<string>();
const searchQueryRef = useRef<string>(undefined);
useEffect(() => {
if ((!parentId || parentId === 'scopes') && searchQuery && config.featureToggles.scopeSearchAllLevels) {
@@ -49,7 +49,7 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) {
const [contentSent, setContentSent] = useState<{ title?: string; folderUid?: string }>({});
const validationTimeoutRef = useRef<NodeJS.Timeout>();
const validationTimeoutRef = useRef<NodeJS.Timeout>(null);
// Validate title on form mount to catch invalid default values
useEffect(() => {
@@ -59,14 +59,18 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) {
// Cleanup timeout on unmount
useEffect(() => {
return () => {
clearTimeout(validationTimeoutRef.current);
if (validationTimeoutRef.current) {
clearTimeout(validationTimeoutRef.current);
}
};
}, []);
const handleTitleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
setValue('title', e.target.value, { shouldDirty: true });
clearTimeout(validationTimeoutRef.current);
if (validationTimeoutRef.current) {
clearTimeout(validationTimeoutRef.current);
}
validationTimeoutRef.current = setTimeout(() => {
trigger('title');
}, 400);
@@ -75,7 +79,9 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) {
);
const onSave = async (overwrite: boolean) => {
clearTimeout(validationTimeoutRef.current);
if (validationTimeoutRef.current) {
clearTimeout(validationTimeoutRef.current);
}
const isTitleValid = await trigger('title');
@@ -8,7 +8,7 @@ import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager';
* Will scroll element into view. If element is not connected yet, it will try to expand rows
* and switch tabs to make it visible.
*/
export function scrollCanvasElementIntoView(sceneObject: SceneObject, ref: React.RefObject<HTMLElement>) {
export function scrollCanvasElementIntoView(sceneObject: SceneObject, ref: React.RefObject<HTMLElement | null>) {
if (ref.current?.isConnected) {
scrollIntoView(ref.current);
return;
@@ -21,7 +21,7 @@ export const usePanelLatestData = (
options: GetDataOptions,
checkSchema?: boolean
): UsePanelLatestData => {
const querySubscription = useRef<Unsubscribable>();
const querySubscription = useRef<Unsubscribable>(null);
const [latestData, setLatestData] = useState<PanelData>();
useEffect(() => {
@@ -61,7 +61,7 @@ interface State {
class UnThemedTransformationsEditor extends React.PureComponent<TransformationsEditorProps, State> {
subscription?: Unsubscribable;
ref: RefObject<HTMLDivElement>;
ref: RefObject<HTMLDivElement | null>;
constructor(props: TransformationsEditorProps) {
super(props);
+1 -1
View File
@@ -64,7 +64,7 @@ export function useDatasource(dataSource: string | DataSourceRef | DataSourceIns
export interface KeybaordNavigatableListProps {
keyboardEvents?: Observable<React.KeyboardEvent>;
containerRef: React.RefObject<HTMLElement>;
containerRef: React.RefObject<HTMLElement | null>;
}
/**
@@ -25,7 +25,7 @@ interface State {
}
export class ThresholdsEditor extends PureComponent<Props, State> {
private latestThresholdInputRef: React.RefObject<HTMLInputElement>;
private latestThresholdInputRef: React.RefObject<HTMLInputElement | null>;
constructor(props: Props) {
super(props);
+3 -1
View File
@@ -640,7 +640,9 @@ export class Explore extends PureComponent<Props, ExploreState> {
)}
<ScrollContainer
data-testid={selectors.pages.Explore.General.scrollView}
ref={(scrollElement) => (this.scrollElement = scrollElement || undefined)}
ref={(scrollElement) => {
this.scrollElement = scrollElement || undefined;
}}
>
<div className={styles.exploreContainer}>
{datasourceInstance ? (
@@ -74,7 +74,7 @@ export const ExplorePaneContainer = connector(ExplorePaneContainerUnconnected);
function useStopQueries(exploreId: string) {
const paneSelector = useMemo(() => getExploreItemSelector(exploreId), [exploreId]);
const paneRef = useRef<ReturnType<typeof paneSelector>>();
const paneRef = useRef<ReturnType<typeof paneSelector>>(null);
paneRef.current = useSelector(paneSelector);
useEffect(() => {
@@ -63,7 +63,7 @@ type Props = {
scrollElementClass?: string;
traceProp: Trace;
datasource: DataSourceApi<DataQuery, DataSourceJsonData, {}> | undefined;
topOfViewRef?: RefObject<HTMLDivElement>;
topOfViewRef?: RefObject<HTMLDivElement | null>;
createSpanLink?: SpanLinkFunc;
focusedSpanId?: string;
createFocusSpanLink?: (traceId: string, spanId: string) => LinkModel<Field>;
@@ -57,7 +57,7 @@ export const SpanFilters = memo((props: SpanFilterProps) => {
const [focusedSpanIndexForSearch, setFocusedSpanIndexForSearch] = useState(-1);
const [tagKeys, setTagKeys] = useState<Array<SelectableValue<string>>>();
const [tagValues, setTagValues] = useState<{ [key: string]: Array<SelectableValue<string>> }>({});
const prevTraceIdRef = useRef<string>();
const prevTraceIdRef = useRef<string>(null);
const durationRegex = /^\d+(?:\.\d)?\d*(?:ns|us|µs|ms|s|m|h)$/;
@@ -561,7 +561,7 @@ const CardsContainer = ({
mainContainerRef,
}: {
listOfContentCards: React.ReactNode[];
mainContainerRef?: React.RefObject<HTMLDivElement>;
mainContainerRef?: React.RefObject<HTMLDivElement | null>;
}) => {
const styles = useStyles2(getStyles);
@@ -197,7 +197,11 @@ export class UnthemedTraceTimelineViewer extends PureComponent<TProps, State> {
return (
<div
className={styles.TraceTimelineViewer}
ref={(ref: HTMLDivElement | null) => ref && this.setState({ height: ref.getBoundingClientRect().height })}
ref={(ref) => {
if (ref) {
this.setState({ height: ref.getBoundingClientRect().height });
}
}}
>
<TimelineHeaderRow
duration={trace.duration}
@@ -43,7 +43,7 @@ export const LibraryPanelsView = ({
}
);
const asyncDispatch = useMemo(() => asyncDispatcher(dispatch), [dispatch]);
const abortControllerRef = useRef<AbortController>();
const abortControllerRef = useRef<AbortController>(null);
useDebounce(
() => {
@@ -12,7 +12,7 @@ export interface Props {}
export const LiveConnectionWarning = memo(function LiveConnectionWarning() {
const [show, setShow] = useState<boolean | undefined>(undefined);
const subscriptionRef = useRef<Unsubscribable>();
const subscriptionRef = useRef<Unsubscribable>(null);
const styles = useStyles2(getStyle);
useEffect(() => {
@@ -38,7 +38,7 @@ export function useSearchKeyboardNavigation(
): ItemSelection {
const highlightIndexRef = useRef<ItemSelection>({ x: 0, y: -1 });
const [highlightIndex, setHighlightIndex] = useState<ItemSelection>({ x: 0, y: -1 });
const urlsRef = useRef<Field>();
const urlsRef = useRef<Field>(null);
// Clear selection when the search results change
useEffect(() => {
@@ -77,7 +77,7 @@ export const SuggestionsInput = ({
const theme = useTheme2();
const styles = getStyles(theme, inputHeight);
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>();
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);
useEffect(() => {
scrollRef.current?.scrollTo(0, scrollTop);
@@ -20,8 +20,8 @@ interface CodeEditorProps {
export const LogsQLCodeEditor = (props: CodeEditorProps) => {
const { query, datasource, onChange } = props;
const monacoRef = useRef<Monaco>();
const disposalRef = useRef<monacoType.IDisposable>();
const monacoRef = useRef<Monaco>(null);
const disposalRef = useRef<monacoType.IDisposable>(undefined);
const onFocus = useCallback(async () => {
disposalRef.current = await reRegisterCompletionProvider(
@@ -42,8 +42,8 @@ interface LogsCodeEditorProps {
export const PPLQueryEditor = (props: LogsCodeEditorProps) => {
const { query, datasource, onChange } = props;
const monacoRef = useRef<Monaco>();
const disposalRef = useRef<monacoType.IDisposable>();
const monacoRef = useRef<Monaco>(null);
const disposalRef = useRef<monacoType.IDisposable>(undefined);
const onFocus = useCallback(async () => {
disposalRef.current = await reRegisterCompletionProvider(
@@ -20,8 +20,8 @@ interface SQLCodeEditorProps {
export const SQLQueryEditor = (props: SQLCodeEditorProps) => {
const { query, datasource, onChange } = props;
const monacoRef = useRef<Monaco>();
const disposalRef = useRef<monacoType.IDisposable>();
const monacoRef = useRef<Monaco>(null);
const disposalRef = useRef<monacoType.IDisposable>(undefined);
const onFocus = useCallback(async () => {
disposalRef.current = await reRegisterCompletionProvider(
@@ -94,7 +94,7 @@ const EDITOR_HEIGHT_OFFSET = 2;
* Hook that returns function that will set up monaco autocomplete for the label selector
*/
function useAutocomplete(getLabelValues: (label: string) => Promise<string[]>, labels?: string[]) {
const providerRef = useRef<CompletionProvider>();
const providerRef = useRef<CompletionProvider>(null);
if (providerRef.current === undefined) {
providerRef.current = new CompletionProvider();
}
@@ -126,7 +126,7 @@ export function LokiContextUi(props: LokiContextUiProps) {
window.localStorage.getItem(SHOULD_INCLUDE_PIPELINE_OPERATIONS) === 'true'
);
const timerHandle = useRef<number>();
const timerHandle = useRef<number>(null);
const previousInitialized = useRef<boolean>(false);
const previousContextFilters = useRef<ContextFilter[]>([]);
@@ -191,14 +191,18 @@ export function LokiContextUi(props: LokiContextUiProps) {
}, 1500);
return () => {
clearTimeout(timerHandle.current);
if (timerHandle.current) {
clearTimeout(timerHandle.current);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [contextFilters, initialized]);
useEffect(() => {
return () => {
clearTimeout(timerHandle.current);
if (timerHandle.current) {
clearTimeout(timerHandle.current);
}
onClose();
};
}, [onClose]);
@@ -52,7 +52,7 @@ export function TraceQLEditor(props: Props) {
const onRunQueryRef = useRef(onRunQuery);
onRunQueryRef.current = onRunQuery;
const errorTimeoutId = useRef<number>();
const errorTimeoutId = useRef<number>(null);
return (
<>
@@ -103,7 +103,9 @@ export function TraceQLEditor(props: Props) {
}
// Remove previous callback if existing, to prevent squiggles from been shown while the user is still typing
window.clearTimeout(errorTimeoutId.current);
if (errorTimeoutId.current) {
window.clearTimeout(errorTimeoutId.current);
}
const errorNodes = getErrorNodes(model.getValue());
const cursorPosition = changeEvent.changes[0].rangeOffset;
@@ -1,5 +1,5 @@
export function renderHistogram(
can: React.RefObject<HTMLCanvasElement>,
can: React.RefObject<HTMLCanvasElement | null>,
histCanWidth: number,
histCanHeight: number,
xVals: number[],
+1 -1
View File
@@ -196,7 +196,7 @@ export const LogsPanel = ({
const dataSourcesMap = useDatasourcesFromTargets(panelData.request?.targets);
// Prevents the scroll position to change when new data from infinite scrolling is received
const keepScrollPositionRef = useRef<null | 'infinite-scroll' | 'user'>(null);
let closeCallback = useRef<() => void>();
let closeCallback = useRef<() => void>(null);
const { app, eventBus, onAddAdHocFilter } = usePanelContext();
useEffect(() => {
+1 -1
View File
@@ -70,7 +70,7 @@ export function useLayout(
const currentSignature = createDataSignature(rawNodes, rawEdges);
const isMounted = useMountedState();
const layoutWorkerCancelRef = useRef<(() => void) | undefined>();
const layoutWorkerCancelRef = useRef<(() => void) | undefined>(null);
useUnmount(() => {
if (layoutWorkerCancelRef.current) {
@@ -31,7 +31,7 @@ interface Options {
*/
export function usePanning<T extends Element>({ scale = 1, bounds, focus }: Options = {}): {
state: State;
ref: RefObject<T>;
ref: RefObject<T | null>;
} {
const isMounted = useMountedState();
const isPanning = useRef(false);
@@ -133,7 +133,7 @@ export const AnnotationsPlugin2 = ({
const newRangeRef = useRef(newRange);
newRangeRef.current = newRange;
const xAxisRef = useRef<HTMLDivElement>();
const xAxisRef = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
config.addHook('ready', (u) => {
@@ -23,7 +23,7 @@ export const ExemplarsPlugin = ({
maxHeight,
maxWidth,
}: ExemplarsPluginProps) => {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot>(null);
const [lockedExemplarRowIndex, setLockedExemplarRowIndex] = useState<number | undefined>();
@@ -11,7 +11,7 @@ interface ThresholdControlsPluginProps {
}
export const OutsideRangePlugin = ({ config, onChangeTimeRange }: ThresholdControlsPluginProps) => {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot>(null);
const [timevalues, setTimeValues] = useState<number[] | TypedArray>([]);
const [timeRange, setTimeRange] = useState<Scale | undefined>();
@@ -16,7 +16,7 @@ interface ThresholdControlsPluginProps {
}
export const ThresholdControlsPlugin = ({ config, fieldConfig, onThresholdsChange }: ThresholdControlsPluginProps) => {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot>(null);
const [renderToken, setRenderToken] = useState(0);
useLayoutEffect(() => {