1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Move all side effects to hooks

Signed-off-by: alexfront <alex.andreev.email@gmail.com>
This commit is contained in:
alexfront 2022-09-08 16:00:01 +03:00
parent ac8ed1478e
commit d951f3a017
6 changed files with 125 additions and 47 deletions

View File

@ -29,6 +29,14 @@
}
}
.firstLine {
width: 100%;
height: 1px;
top: 0;
background-color: var(--logsBackground);
position: absolute;
}
.lastLine {
width: 100%;
height: 1px;

View File

@ -5,18 +5,19 @@
import styles from "./log-list.module.scss";
import throttle from "lodash/throttle";
import { useVirtualizer } from '@tanstack/react-virtual';
import { observer } from 'mobx-react';
import React, { useEffect, useRef } from 'react';
import type { LogTabViewModel } from './logs-view-model';
import { LogRow } from "./log-row";
import React, { useRef } from 'react';
import { cssNames } from "../../../utils";
import { v4 as getRandomId } from "uuid";
import { useJumpToBottomButton } from "./use-scroll-to-bottom";
import { useInitialScrollToBottom } from "./use-initial-scroll-to-bottom";
import { LogRow } from "./log-row";
import type { LogTabViewModel } from './logs-view-model';
import { ToBottom } from "./to-bottom";
import useIntersectionObserver from "../../../hooks/useIntersectionObserver";
import { useInitialScrollToBottom } from "./use-initial-scroll-to-bottom";
import { useOnScrollTop } from "./use-on-scroll-top";
import { useRefreshListOnDataChange } from "./use-refresh-list-on-data-change";
import { useScrollOnSearch } from "./use-scroll-on-search";
import { useJumpToBottomButton } from "./use-scroll-to-bottom";
import { useStickToBottomOnLogsLoad } from "./use-stick-to-bottom-on-logs-load";
export interface LogListProps {
model: LogTabViewModel;
@ -25,10 +26,9 @@ export interface LogListProps {
export const LogList = observer(({ model }: LogListProps) => {
const { visibleLogs } = model;
const parentRef = useRef<HTMLDivElement>(null);
const lastLineRef = useRef<HTMLDivElement>(null);
const [rowKeySuffix, setRowKeySuffix] = React.useState(getRandomId());
const topLineRef = useRef<HTMLDivElement>(null);
const bottomLineRef = useRef<HTMLDivElement>(null);
const [toBottomVisible, setButtonVisibility] = useJumpToBottomButton(parentRef.current);
const entry = useIntersectionObserver(lastLineRef.current, {});
const rowVirtualizer = useVirtualizer({
count: visibleLogs.get().length,
@ -45,50 +45,21 @@ export const LogList = observer(({ model }: LogListProps) => {
scrollTo(visibleLogs.get().length - 1);
}
const onScroll = throttle(() => {
const onScroll = () => {
if (!parentRef.current) return;
setButtonVisibility();
onScrollToTop();
}, 1_000, { trailing: true, leading: true });
/**
* Loads new logs if user scrolled to the top
*/
const onScrollToTop = async () => {
const { scrollTop } = parentRef.current as HTMLDivElement;
if (scrollTop === 0) {
const logs = model.logs.get();
const firstLog = logs[0];
await model.loadLogs();
const scrollToIndex = model.logs.get().findIndex(log => log === firstLog);
scrollTo(scrollToIndex);
}
};
useInitialScrollToBottom(model, scrollToBottom);
useEffect(() => {
// rowVirtualizer.scrollToIndex(visibleLogs.get().length - 1, { align: 'end', smoothScroll: false });
// Refresh list
setRowKeySuffix(getRandomId());
}, [model.logTabData.get()]);
const uniqRowKey = useRefreshListOnDataChange(model.logTabData.get());
useEffect(() => {
if (!model.searchStore.occurrences.length) return;
useScrollOnSearch(model.searchStore, scrollTo);
scrollTo(model.searchStore.occurrences[model.searchStore.activeOverlayIndex]);
}, [model.searchStore.searchQuery, model.searchStore.activeOverlayIndex])
useStickToBottomOnLogsLoad({ bottomLineRef, model, scrollToBottom });
useEffect(() => {
if (entry?.isIntersecting) {
scrollToBottom();
}
}, [model.visibleLogs.get().length]);
useOnScrollTop({ topLineRef, model, scrollTo });
return (
<div
@ -102,9 +73,10 @@ export const LogList = observer(({ model }: LogListProps) => {
}}
className={styles.virtualizer}
>
<div className={styles.firstLine} ref={topLineRef}></div>
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
<div
key={virtualRow.index + rowKeySuffix}
key={virtualRow.index + uniqRowKey}
ref={virtualRow.measureElement}
style={{
transform: `translateY(${virtualRow.start}px)`,
@ -116,7 +88,7 @@ export const LogList = observer(({ model }: LogListProps) => {
</div>
</div>
))}
<div className={styles.lastLine} ref={lastLineRef}></div>
<div className={styles.lastLine} ref={bottomLineRef}></div>
</div>
{toBottomVisible && (
<ToBottom onClick={scrollToBottom} />

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { RefObject } from "react";
import { useEffect } from "react";
import useIntersectionObserver from "../../../hooks/useIntersectionObserver";
import type { LogTabViewModel } from "./logs-view-model";
interface UseStickToBottomProps {
topLineRef: RefObject<HTMLDivElement>;
model: LogTabViewModel;
scrollTo: (index: number) => void;
}
export function useOnScrollTop({ topLineRef, model, scrollTo }: UseStickToBottomProps) {
const topLineEntry = useIntersectionObserver(topLineRef.current, {});
function getPreviouslyFirstLogIndex(firstLog: string) {
return model.logs.get().findIndex(log => log === firstLog);
}
async function onScrolledTop() {
const firstLog = model.logs.get()[0];
const scrollIndex = () => getPreviouslyFirstLogIndex(firstLog);
await model.loadLogs();
scrollTo(scrollIndex());
}
useEffect(() => {
if (topLineEntry?.isIntersecting) {
onScrolledTop();
}
}, [topLineEntry?.isIntersecting]);
}

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { useEffect, useState } from "react";
import type { LogTabData } from "./tab-store";
import { v4 as getRandomId } from "uuid";
export function useRefreshListOnDataChange(data: LogTabData | undefined) {
const [rowKeySuffix, setRowKeySuffix] = useState(getRandomId());
useEffect(() => {
// Refresh virtualizer list rows by changing their keys
setRowKeySuffix(getRandomId());
}, [data]);
return rowKeySuffix;
}

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { useEffect } from "react";
import type { SearchStore } from "../../../search-store/search-store";
export function useScrollOnSearch(store: SearchStore, scrollTo: (index: number) => void) {
const { occurrences, searchQuery, activeOverlayIndex } = store;
useEffect(() => {
if (!occurrences.length) return;
scrollTo(occurrences[activeOverlayIndex]);
}, [searchQuery, activeOverlayIndex]);
}

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { RefObject } from "react";
import { useEffect } from "react";
import useIntersectionObserver from "../../../hooks/useIntersectionObserver";
import type { LogTabViewModel } from "./logs-view-model";
interface UseStickToBottomProps {
bottomLineRef: RefObject<HTMLDivElement>;
model: LogTabViewModel;
scrollToBottom: () => void;
}
export function useStickToBottomOnLogsLoad({ bottomLineRef, model, scrollToBottom }: UseStickToBottomProps) {
const bottomLineEntry = useIntersectionObserver(bottomLineRef.current, {});
useEffect(() => {
if (bottomLineEntry?.isIntersecting) {
scrollToBottom();
}
}, [model.visibleLogs.get().length]);
}