From b0bcb7130cf0daeeb55f339b0e6add9ad4a33761 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 26 Oct 2020 22:27:21 +0800 Subject: [PATCH] refactor: prevent rendering multiple times on search --- src/components/FileExplorer.tsx | 61 +++++++++++++++++------------- src/components/Icon.tsx | 9 ++++- src/driver/core/FileExplorer.ts | 4 +- src/utils/VisibleNodesGenerator.ts | 35 +++++++++++++---- 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index 5ce60d7..1f30317 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -42,9 +42,30 @@ const RawFileExplorer: React.FC = function RawFileExplor if (visibleNodes?.focusedNode) focusFileExplorer() }) + const renderActions: ((node: TreeNode) => React.ReactNode) | undefined = React.useMemo( + () => + visibleNodes?.lastMatch?.match.searchKey + ? node => ( + + ) + : undefined, + [visibleNodes, goTo], + ) + function renderFiles(visibleNodes: VisibleNodes) { - const inSearch = searchKey !== '' const { nodes } = visibleNodes + const searchKey = visibleNodes.lastMatch?.match.searchKey + const inSearch = searchKey !== '' if (inSearch && nodes.length === 0) { return ( @@ -58,25 +79,8 @@ const RawFileExplorer: React.FC = function RawFileExplor ( - - ) - : undefined - } + renderActions={renderActions} visibleNodes={visibleNodes} expandTo={expandTo} metaData={metaData} @@ -132,11 +136,19 @@ const VirtualNode = React.memo(function VirtualNode({ style, data, }: ListChildComponentProps) { - const { regex, onNodeClick, renderActions, visibleNodes } = data + const { onNodeClick, renderActions, visibleNodes } = data if (!visibleNodes) return null - const { nodes, focusedNode, expandedNodes, loading, depths } = visibleNodes as VisibleNodes + const { + lastMatch, + nodes, + focusedNode, + expandedNodes, + loading, + depths, + } = visibleNodes as VisibleNodes const node = nodes[index] + const searchKey = lastMatch?.match.searchKey return ( ) }) @@ -156,7 +168,6 @@ const VirtualNode = React.memo(function VirtualNode({ type ListViewProps = { height: number width: number - searchKey: string onNodeClick(event: React.MouseEvent, node: TreeNode): void renderActions?(node: TreeNode): React.ReactNode visibleNodes: VisibleNodes @@ -167,7 +178,6 @@ function ListView({ height, metaData, expandTo, - searchKey, onNodeClick, renderActions, visibleNodes, @@ -193,12 +203,11 @@ function ListView({ const itemData = React.useMemo( () => ({ - regex: searchKey && isValidRegexpSource(searchKey) ? new RegExp(searchKey, 'gi') : undefined, onNodeClick, renderActions, visibleNodes, }), - [searchKey, onNodeClick, renderActions, visibleNodes], + [onNodeClick, renderActions, visibleNodes], ) return ( diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index b628315..e14e7a5 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -137,7 +137,12 @@ type Props = { onClick?: (event: React.MouseEvent) => void } & IconProps -export function Icon({ type, className = undefined, placeholder, ...otherProps }: Props) { +export const Icon = React.memo(function Icon({ + type, + className = undefined, + placeholder, + ...otherProps +}: Props) { let children: React.ReactNode = null if (!placeholder) { const { name, IconComponent } = getSVGIconComponent(type) @@ -148,4 +153,4 @@ export function Icon({ type, className = undefined, placeholder, ...otherProps } {children} ) -} +}) diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index 57adff0..070355f 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -2,7 +2,6 @@ import { GetCreatedMethod, MethodCreator } from 'driver/connect' import { platform } from 'platforms' import { Config } from 'utils/configHelper' import * as DOMHelper from 'utils/DOMHelper' -import { searchKeyToRegexp } from 'utils/general' import { VisibleNodes, VisibleNodesGenerator } from 'utils/VisibleNodesGenerator' export type Props = { @@ -182,8 +181,7 @@ export const onFocusSearchBar: BoundMethodCreator = dispatch => () => dispatch.c export const search: BoundMethodCreator<[string]> = dispatch => searchKey => { dispatch.set({ searchKey, searched: searchKey !== '' }) - const regexp = searchKeyToRegexp(searchKey) - visibleNodesGenerator.search(regexp && (node => regexp.test(node.name))) + visibleNodesGenerator.search({ searchKey }) } export const goTo: BoundMethodCreator<[string[]]> = dispatch => currentPath => { diff --git a/src/utils/VisibleNodesGenerator.ts b/src/utils/VisibleNodesGenerator.ts index ead07d9..a86c0c6 100644 --- a/src/utils/VisibleNodesGenerator.ts +++ b/src/utils/VisibleNodesGenerator.ts @@ -1,5 +1,5 @@ import { EventHub } from './EventHub' -import { findNode, traverse, withEffect } from './general' +import { findNode, searchKeyToRegexp, traverse, withEffect } from './general' function search( root: TreeNode, @@ -131,12 +131,27 @@ class ShakeLayer extends BaseLayer { } shake = withEffect( - (p?: { match: (node: TreeNode) => boolean; onChildMatch: (node: TreeNode) => void }) => { + (p?: { + match: { + // shape in object for better extensibility + searchKey: string + } + onChildMatch: (node: TreeNode) => void + }) => { this.lastMatch = p if (p) { - const { match, onChildMatch } = p - this.shackedRoot = search(this.baseRoot, match, onChildMatch) - } else this.shackedRoot = this.baseRoot + const { + match: { searchKey }, + onChildMatch, + } = p + + const regexp = searchKeyToRegexp(searchKey) + if (regexp) { + this.shackedRoot = search(this.baseRoot, node => regexp.test(node.name), onChildMatch) + return + } + } + this.shackedRoot = this.baseRoot }, () => this.shakeHub.emit('emit', this.shackedRoot), ) @@ -232,7 +247,7 @@ class FlattenLayer extends CompressLayer { focusNode = (node: TreeNode | null) => { if (this.focusedNode !== node) { - this.focusedNode = node + this.focusedNode = node this.flattenHub.emit('emit', null) } } @@ -284,7 +299,11 @@ class FlattenLayer extends CompressLayer { } }, this.generateVisibleNodes) - search = (match: ((node: TreeNode) => boolean) | null) => { + search = ( + match: { + searchKey: string + } | null, + ) => { // this.focusNode(null) this.shake( match @@ -305,6 +324,7 @@ type Options = { export type VisibleNodes = { loading: BaseLayer['loading'] + lastMatch: ShakeLayer['lastMatch'] depths: CompressLayer['depths'] nodes: FlattenLayer['nodes'] expandedNodes: FlattenLayer['expandedNodes'] @@ -333,6 +353,7 @@ export class VisibleNodesGenerator extends FlattenLayer { get visibleNodes(): VisibleNodes { return { nodes: this.nodes, + lastMatch: this.lastMatch, depths: this.depths, expandedNodes: this.expandedNodes, focusedNode: this.focusedNode,