chore: migrate to useRef take 2 (#21552)

This commit is contained in:
Pavel Feldman 2023-03-09 19:34:05 -08:00 committed by GitHub
parent 7a1c5b2aa3
commit e737ff83b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 66 deletions

View File

@ -37,7 +37,7 @@ export const HeaderView: React.FC<React.PropsWithChildren<{
setFilterText(params.get('q') || '');
});
})();
});
}, [setFilterText]);
return (<>
<div className='pt-3'>

View File

@ -30,7 +30,7 @@ export const CallLogView: React.FC<CallLogProps> = ({
language,
log,
}) => {
const messagesEndRef = React.createRef<HTMLDivElement>();
const messagesEndRef = React.useRef<HTMLDivElement>(null);
const [expandOverrides, setExpandOverrides] = React.useState<Map<string, boolean>>(new Map());
React.useLayoutEffect(() => {
if (log.find(callLog => callLog.reveal))

View File

@ -78,7 +78,7 @@ export const Recorder: React.FC<RecorderProps> = ({
setFileId(value);
};
const messagesEndRef = React.createRef<HTMLDivElement>();
const messagesEndRef = React.useRef<HTMLDivElement>(null);
React.useLayoutEffect(() => {
messagesEndRef.current?.scrollIntoView({ block: 'center', inline: 'nearest' });
}, [messagesEndRef]);

View File

@ -41,35 +41,37 @@ export const SnapshotTab: React.FunctionComponent<{
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
const [pickerVisible, setPickerVisible] = React.useState(false);
const snapshotMap = new Map<string, { title: string, snapshotName: string }>();
for (const snapshot of action?.snapshots || [])
snapshotMap.set(snapshot.title, snapshot);
const actionSnapshot = snapshotMap.get('action') || snapshotMap.get('after');
const snapshots = [actionSnapshot ? { ...actionSnapshot, title: 'action' } : undefined, snapshotMap.get('before'), snapshotMap.get('after')].filter(Boolean) as { title: string, snapshotName: string }[];
let snapshotUrl = 'data:text/html,<body style="background: #ddd"></body>';
let popoutUrl: string | undefined;
let snapshotInfoUrl: string | undefined;
let pointX: number | undefined;
let pointY: number | undefined;
if (action) {
const snapshot = snapshots[snapshotIndex];
if (snapshot && snapshot.snapshotName) {
const params = new URLSearchParams();
params.set('trace', context(action).traceUrl);
params.set('name', snapshot.snapshotName);
snapshotUrl = new URL(`snapshot/${action.pageId}?${params.toString()}`, window.location.href).toString();
snapshotInfoUrl = new URL(`snapshotInfo/${action.pageId}?${params.toString()}`, window.location.href).toString();
if (snapshot.snapshotName.includes('action')) {
pointX = action.point?.x;
pointY = action.point?.y;
const { snapshots, snapshotInfoUrl, snapshotUrl, pointX, pointY, popoutUrl } = React.useMemo(() => {
const snapshotMap = new Map<string, { title: string, snapshotName: string }>();
for (const snapshot of action?.snapshots || [])
snapshotMap.set(snapshot.title, snapshot);
const actionSnapshot = snapshotMap.get('action') || snapshotMap.get('after');
const snapshots = [actionSnapshot ? { ...actionSnapshot, title: 'action' } : undefined, snapshotMap.get('before'), snapshotMap.get('after')].filter(Boolean) as { title: string, snapshotName: string }[];
let snapshotUrl = 'data:text/html,<body style="background: #ddd"></body>';
let popoutUrl: string | undefined;
let snapshotInfoUrl: string | undefined;
let pointX: number | undefined;
let pointY: number | undefined;
if (action) {
const snapshot = snapshots[snapshotIndex];
if (snapshot && snapshot.snapshotName) {
const params = new URLSearchParams();
params.set('trace', context(action).traceUrl);
params.set('name', snapshot.snapshotName);
snapshotUrl = new URL(`snapshot/${action.pageId}?${params.toString()}`, window.location.href).toString();
snapshotInfoUrl = new URL(`snapshotInfo/${action.pageId}?${params.toString()}`, window.location.href).toString();
if (snapshot.snapshotName.includes('action')) {
pointX = action.point?.x;
pointY = action.point?.y;
}
const popoutParams = new URLSearchParams();
popoutParams.set('r', snapshotUrl);
popoutParams.set('trace', context(action).traceUrl);
popoutUrl = new URL(`popout.html?${popoutParams.toString()}`, window.location.href).toString();
}
const popoutParams = new URLSearchParams();
popoutParams.set('r', snapshotUrl);
popoutParams.set('trace', context(action).traceUrl);
popoutUrl = new URL(`popout.html?${popoutParams.toString()}`, window.location.href).toString();
}
}
return { snapshots, snapshotInfoUrl, snapshotUrl, pointX, pointY, popoutUrl };
}, [action, snapshotIndex]);
React.useEffect(() => {
if (snapshots.length >= 1 && snapshotIndex >= snapshots.length)
@ -155,8 +157,9 @@ export const SnapshotTab: React.FunctionComponent<{
setIsInspecting(!isInspecting);
}}></ToolbarButton>
<CodeMirrorWrapper text={highlightedLocator} language={sdkLanguage} readOnly={!popoutUrl} focusOnChange={true} wrapLines={true} onChange={text => {
setIsInspecting(false);
// Updating text needs to go first - react can squeeze a render between the state updates.
setHighlightedLocator(text);
setIsInspecting(false);
}}></CodeMirrorWrapper>
<ToolbarButton icon='files' title='Copy locator' disabled={!popoutUrl} onClick={() => {
copy(highlightedLocator);

View File

@ -70,7 +70,7 @@ export const SourceTab: React.FunctionComponent<{
const targetLine = typeof stackInfo === 'string' ? 0 : stackInfo.frames[selectedFrame]?.line || 0;
const targetLineRef = React.createRef<HTMLDivElement>();
const targetLineRef = React.useRef<HTMLDivElement>(null);
React.useLayoutEffect(() => {
if (needReveal && targetLineRef.current) {
targetLineRef.current.scrollIntoView({ block: 'center', inline: 'nearest' });

View File

@ -42,15 +42,16 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
text,
language,
readOnly,
highlight = [],
highlight,
revealLine,
lineNumbers,
focusOnChange,
wrapLines,
onChange,
}) => {
const codemirrorElement = React.createRef<HTMLDivElement>();
const codemirrorElement = React.useRef<HTMLDivElement>(null);
const [modulePromise] = React.useState<Promise<CodeMirror>>(import('./codeMirrorModule').then(m => m.default));
const codemirrorRef = React.useRef<CodeMirror.Editor|null>(null);
const [codemirror, setCodemirror] = React.useState<CodeMirror.Editor>();
React.useEffect(() => {
@ -70,18 +71,17 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
if (language === 'csharp')
mode = 'text/x-csharp';
if (codemirror
&& mode === codemirror.getOption('mode')
&& readOnly === codemirror.getOption('readOnly')
&& lineNumbers === codemirror.getOption('lineNumbers')
&& wrapLines === codemirror.getOption('lineWrapping')) {
updateEditor(codemirror, text, highlight, revealLine, focusOnChange);
if (codemirrorRef.current
&& mode === codemirrorRef.current.getOption('mode')
&& readOnly === codemirrorRef.current.getOption('readOnly')
&& lineNumbers === codemirrorRef.current.getOption('lineNumbers')
&& wrapLines === codemirrorRef.current.getOption('lineWrapping')) {
// No need to re-create codemirror.
return;
}
// Either configuration is different or we don't have a codemirror yet.
if (codemirror)
codemirror.getWrapperElement().remove();
codemirrorRef.current?.getWrapperElement().remove();
const cm = CodeMirror(element, {
value: '',
@ -90,29 +90,38 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
lineNumbers,
lineWrapping: wrapLines,
});
codemirrorRef.current = cm;
setCodemirror(cm);
if (onChange)
cm.on('change', () => onChange(cm.getValue()));
updateEditor(cm, text, highlight, revealLine, focusOnChange);
return cm;
})();
}, [modulePromise, codemirror, codemirrorElement, text, language, highlight, revealLine, focusOnChange, lineNumbers, wrapLines, readOnly, onChange]);
}, [modulePromise, codemirror, codemirrorElement, language, lineNumbers, wrapLines, readOnly]);
React.useEffect(() => {
if (!codemirror)
return;
codemirror.off('change', (codemirror as any).listenerSymbol);
(codemirror as any)[listenerSymbol] = undefined;
if (onChange) {
(codemirror as any)[listenerSymbol] = () => onChange(codemirror.getValue());
codemirror.on('change', (codemirror as any)[listenerSymbol]);
}
if (codemirror.getValue() !== text) {
codemirror.setValue(text);
if (focusOnChange) {
codemirror.execCommand('selectAll');
codemirror.focus();
}
}
for (let i = 0; i < codemirror.lineCount(); ++i)
codemirror.removeLineClass(i, 'wrap');
for (const h of highlight || [])
codemirror.addLineClass(h.line - 1, 'wrap', `source-line-${h.type}`);
if (revealLine)
codemirror.scrollIntoView({ line: revealLine - 1, ch: 0 }, 50);
}, [codemirror, text, highlight, revealLine, focusOnChange, onChange]);
return <div className='cm-wrapper' ref={codemirrorElement}></div>;
};
function updateEditor(cm: CodeMirror.Editor, text: string, highlight: SourceHighlight[], revealLine?: number, focusOnChange?: boolean) {
if (cm.getValue() !== text) {
cm.setValue(text);
if (focusOnChange) {
cm.execCommand('selectAll');
cm.focus();
}
}
for (let i = 0; i < cm.lineCount(); ++i)
cm.removeLineClass(i, 'wrap');
for (const h of highlight)
cm.addLineClass(h.line - 1, 'wrap', `source-line-${h.type}`);
if (revealLine)
cm.scrollIntoView({ line: revealLine - 1, ch: 0 }, 50);
}
const listenerSymbol = Symbol('listener');

View File

@ -52,7 +52,7 @@ export function ListView<T>({
noItemsMessage,
dataTestId,
}: ListViewProps<T>) {
const itemListRef = React.createRef<HTMLDivElement>();
const itemListRef = React.useRef<HTMLDivElement>(null);
const [highlightedItem, setHighlightedItem] = React.useState<any>();
React.useEffect(() => {

View File

@ -30,9 +30,9 @@ export type XtermDataSource = {
export const XtermWrapper: React.FC<{ source: XtermDataSource }> = ({
source
}) => {
const xtermElement = React.createRef<HTMLDivElement>();
const xtermElement = React.useRef<HTMLDivElement>(null);
const [modulePromise] = React.useState<Promise<XtermModule>>(import('./xtermModule').then(m => m.default));
const [terminal, setTerminal] = React.useState<Terminal>();
const terminal = React.useRef<Terminal | null>(null);
React.useEffect(() => {
(async () => {
// Always load the module first.
@ -41,7 +41,7 @@ export const XtermWrapper: React.FC<{ source: XtermDataSource }> = ({
if (!element)
return;
if (terminal)
if (terminal.current)
return;
const newTerminal = new Terminal({
@ -65,7 +65,7 @@ export const XtermWrapper: React.FC<{ source: XtermDataSource }> = ({
};
newTerminal.open(element);
fitAddon.fit();
setTerminal(newTerminal);
terminal.current = newTerminal;
const resizeObserver = new ResizeObserver(() => {
source.resize(newTerminal.cols, newTerminal.rows);
fitAddon.fit();