refactor: no import * as from react

This commit is contained in:
EnixCoda 2024-09-23 18:38:43 +08:00
parent c3de9ad835
commit 03e3454afc
73 changed files with 250 additions and 251 deletions

View file

@ -2,7 +2,7 @@ import { useConfigs } from 'containers/ConfigsContext'
import { GITHUB_OAUTH } from 'env'
import { platform } from 'platforms'
import { GitHub } from 'platforms/GitHub'
import * as React from 'react'
import React from 'react'
export function AccessDeniedDescription() {
const { accessToken } = useConfigs().value

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { cx } from 'utils/cx'
import { copyElementContent } from 'utils/DOMHelper'
@ -10,8 +10,8 @@ const className = 'clippy-wrapper'
export const ClippyClassName = className
export function Clippy({ codeSnippetElement }: Props) {
const [state, setState] = React.useState<'normal' | 'success' | 'fail'>('normal')
React.useEffect(() => {
const [state, setState] = useState<'normal' | 'success' | 'fail'>('normal')
useEffect(() => {
const timer = window.setTimeout(() => {
setState('normal')
}, 1000)
@ -21,8 +21,8 @@ export function Clippy({ codeSnippetElement }: Props) {
// Temporary fix:
// React moved root node of event delegation since v17
// onClick on <a /> won't work when rendered with `renderReact`
const elementRef = React.useRef<HTMLButtonElement | null>(null)
React.useEffect(() => {
const elementRef = useRef<HTMLButtonElement | null>(null)
useEffect(() => {
const element = elementRef.current
if (element) {
const onClippyClick = () =>

View file

@ -5,7 +5,7 @@ import {
DiffRemovedIcon,
DiffRenamedIcon,
} from '@primer/octicons-react'
import * as React from 'react'
import React from 'react'
import { resolveDiffGraphMeta } from 'utils/general'
import { Icon } from '../Icon'

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React from 'react'
import { Icon } from '../Icon'
const iconMap = {

View file

@ -1,6 +1,6 @@
import { useConfigs } from 'containers/ConfigsContext'
import { platform } from 'platforms'
import * as React from 'react'
import React, { useMemo, useRef } from 'react'
import { cx } from 'utils/cx'
import { cancelEvent } from 'utils/DOMHelper'
import { getFileIconURL, getFolderIconURL } from 'utils/parseIconMapCSV'
@ -43,7 +43,7 @@ export const Node = React.memo(function Node({
onFocus,
}: Props) {
const { compactFileTree: compact } = useConfigs().value
const ref = React.useRef<HTMLDivElement>(null)
const ref = useRef<HTMLDivElement>(null)
return (
<a
href={node.url}
@ -87,11 +87,11 @@ const NodeItemIcon = React.memo(function NodeItemIcon({
}) {
const { icons } = useConfigs().value
const src = React.useMemo(
const src = useMemo(
() => (node.type === 'tree' ? getFolderIconURL(node, open) : getFileIconURL(node)),
[node, open],
)
const iconType = React.useMemo(() => getIconType(node), [node])
const iconType = useMemo(() => getIconType(node), [node])
if (icons === 'native') return <Icon type={iconType} />
return (

View file

@ -1,8 +1,8 @@
import * as React from 'react'
import { useCallback } from 'react'
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
export function useExpandTo(visibleNodesGenerator: VisibleNodesGenerator) {
return React.useCallback(
return useCallback(
async (currentPath: string[]) => {
const nodeExpandedTo = await visibleNodesGenerator.expandTo(currentPath.join('/'))
if (nodeExpandedTo) visibleNodesGenerator.focusNode(nodeExpandedTo)

View file

@ -1,8 +1,8 @@
import * as React from 'react'
import { useCallback } from 'react'
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
export function useFocusNode(visibleNodesGenerator: VisibleNodesGenerator) {
return React.useCallback(
return useCallback(
(node: TreeNode | null) => visibleNodesGenerator.focusNode(node),
[visibleNodesGenerator],
)

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React, { useCallback } from 'react'
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
import { useExpandTo } from './useExpandTo'
@ -7,7 +7,7 @@ export function useGoTo(
updateSearchKey: React.Dispatch<React.SetStateAction<string>>,
expandTo: ReturnType<typeof useExpandTo>,
) {
return React.useCallback(
return useCallback(
(path: string[]) => {
updateSearchKey('')
visibleNodesGenerator.search(null)

View file

@ -1,5 +1,5 @@
import { SidebarContext } from 'components/SidebarContext'
import * as React from 'react'
import React, { useCallback, useContext } from 'react'
import * as DOMHelper from 'utils/DOMHelper'
import { OperatingSystems, os } from 'utils/general'
import { loadWithFastRedirect } from 'utils/hooks/useFastRedirect'
@ -32,9 +32,9 @@ export function useHandleKeyDown(
searched: boolean,
setAlignMode: (mode: AlignMode) => void,
) {
const { pendingFocusTarget } = React.useContext(SidebarContext)
const { pendingFocusTarget } = useContext(SidebarContext)
const setPendingFocusTarget = pendingFocusTarget.onChange
return React.useCallback(
return useCallback(
(event: React.KeyboardEvent<HTMLElement>) => {
const { nodes, focusedNode, expandedNodes } = visibleNodes

View file

@ -8,7 +8,7 @@ import { ActionList, AnchoredOverlay } from '@primer/react'
import { useConfigs } from 'containers/ConfigsContext'
import { PortalContext } from 'containers/PortalContext'
import { platform } from 'platforms'
import * as React from 'react'
import React, { useCallback, useContext, useMemo, useState } from 'react'
import { useCopyToClipboard } from 'react-use'
import { cx } from 'utils/cx'
import { cancelEvent, onEnterKeyDown } from 'utils/DOMHelper'
@ -22,7 +22,7 @@ import { VisibleNodesGeneratorMethods } from './useVisibleNodesGeneratorMethods'
export type NodeRenderer = (node: TreeNode) => React.ReactNode
export function useNodeRenderers(allRenderers: (NodeRenderer | null | undefined)[]) {
return React.useMemo(() => {
return useMemo(() => {
const renderers: NodeRenderer[] = allRenderers.filter(is.not.nil)
return renderers.length
? (node: TreeNode) =>
@ -33,7 +33,7 @@ export function useNodeRenderers(allRenderers: (NodeRenderer | null | undefined)
export function useRenderFileStatus() {
const { showDiffInText } = useConfigs().value
return React.useCallback(
return useCallback(
function renderFileStatus({ diff }: TreeNode) {
return (
diff && (
@ -64,10 +64,10 @@ function NodeContextMenu({
node: TreeNode
visibleNodesGeneratorMethods: VisibleNodesGeneratorMethods
}) {
const [isOpen, setIsOpen] = React.useState(false)
const [copied, setCopied] = React.useState<string | null>(null)
const [isOpen, setIsOpen] = useState(false)
const [copied, setCopied] = useState<string | null>(null)
const [copyState, copyToClipboard] = useCopyToClipboard()
const portalName = React.useContext(PortalContext)
const portalName = useContext(PortalContext)
const actionElements = {
copyPermalink:
node.permalink &&
@ -233,14 +233,14 @@ export function useRenderFileCommentAmounts() {
) : null
}
const { commentToggle } = useConfigs().value
return React.useMemo(() => (commentToggle ? renderFileCommentAmounts : null), [commentToggle])
return useMemo(() => (commentToggle ? renderFileCommentAmounts : null), [commentToggle])
}
export function useRenderFindInFolderButton(
onSearch: (searchKey: string, searchMode: SearchMode) => void,
) {
const { searchMode } = useConfigs().value
return React.useMemo(
return useMemo(
() =>
searchMode === 'fuzzy'
? function renderFindInFolderButton(node: TreeNode) {
@ -260,7 +260,7 @@ export function useRenderFindInFolderButton(
}
export function useRenderGoToButton(searched: boolean, goTo: (path: string[]) => void) {
return React.useMemo(
return useMemo(
() =>
searched
? function renderGoToButton(node: TreeNode): React.ReactNode {

View file

@ -1,5 +1,5 @@
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import React, { useCallback } from 'react'
import { isOpenInNewWindowClick } from 'utils/general'
import { loadWithFastRedirect } from 'utils/hooks/useFastRedirect'
import { AlignMode } from '../useVirtualScroll'
@ -10,7 +10,7 @@ export function useHandleNodeClick(
setAlignMode: (mode: AlignMode) => void,
) {
const { recursiveToggleFolder } = useConfigs().value
return React.useCallback(
return useCallback(
(event: React.MouseEvent<HTMLElement, MouseEvent>, node: TreeNode) => {
setAlignMode('lazy')
switch (node.type) {

View file

@ -1,5 +1,5 @@
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { useCallback } from 'react'
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
import { SearchMode, searchModes } from '../../searchModes'
@ -8,7 +8,7 @@ export function useOnSearch(
visibleNodesGenerator: VisibleNodesGenerator,
) {
const { restoreExpandedFolders } = useConfigs().value
return React.useCallback(
return useCallback(
(searchKey: string, searchMode: SearchMode) => {
updateSearchKey(searchKey)
visibleNodesGenerator.search(

View file

@ -1,10 +1,10 @@
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { useCallback } from 'react'
import { searchModes } from '../../searchModes'
export function useRenderLabelText(searchKey: string) {
const { searchMode } = useConfigs().value
return React.useCallback(
return useCallback(
(node: TreeNode) => searchModes[searchMode].renderNodeLabelText(node, searchKey),
[searchKey, searchMode],
)

View file

@ -1,8 +1,8 @@
import * as React from 'react'
import { useCallback } from 'react'
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
export function useToggleExpansion(visibleNodesGenerator: VisibleNodesGenerator) {
return React.useCallback(
return useCallback(
async (
node: TreeNode,
{

View file

@ -3,10 +3,18 @@ import { useFocusOnPendingTarget } from 'components/FocusTarget'
import { LoadingIndicator } from 'components/LoadingIndicator'
import { SearchBar } from 'components/SearchBar'
import { useConfigs } from 'containers/ConfigsContext'
import { useInspector } from 'containers/Inspector'
import { PortalContext } from 'containers/PortalContext'
import { RepoContext } from 'containers/RepoContext'
import { useInspector } from 'containers/Inspector'
import * as React from 'react'
import React, {
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react'
import { usePrevious, useUpdateEffect } from 'react-use'
import { cx } from 'utils/cx'
import { run } from 'utils/general'
@ -46,7 +54,7 @@ export type NodeRendererContext = {
}
export function FileExplorer() {
const metaData = React.useContext(RepoContext)
const metaData = useContext(RepoContext)
const visibleNodesGenerator = useVisibleNodesGenerator(metaData)
const visibleNodes = useVisibleNodes(visibleNodesGenerator)
const state = useLoadedContext(SideBarStateContext).value
@ -88,7 +96,7 @@ function LoadedFileExplorer({
}) {
const config = useConfigs().value
const [searchKey, updateSearchKey] = React.useState('')
const [searchKey, updateSearchKey] = useState('')
const searched = !!searchKey
const onSearch = useOnSearch(updateSearchKey, visibleNodesGenerator)
const { focusedNode, nodes, expandedNodes, depths, loading } = visibleNodes
@ -117,8 +125,8 @@ function LoadedFileExplorer({
overScan: 10,
})
const portalName = React.useMemo(() => `${Math.random()}`, [])
React.useEffect(() => {
const portalName = useMemo(() => `${Math.random()}`, [])
useEffect(() => {
const current = scrollElementRef.current
if (current) registerPortalRoot(current, portalName)
}, [scrollElementRef, portalName])
@ -133,18 +141,18 @@ function LoadedFileExplorer({
// - "lazy"
// - navigate with keyboard
// - "lazy"
const [alignMode, setAlignMode] = React.useState<AlignMode>('top')
const [alignMode, setAlignMode] = useState<AlignMode>('top')
const index = React.useMemo(
const index = useMemo(
() => (focusedNode?.path ? nodes.findIndex(node => node.path === focusedNode.path) : -1),
[focusedNode?.path, nodes],
)
React.useLayoutEffect(() => {
useLayoutEffect(() => {
if (index !== -1) scrollToItem(index, alignMode)
}, [index, scrollToItem, alignMode])
const prevSearchKey = usePrevious(searchKey)
React.useEffect(() => {
useEffect(() => {
// when start searching or stop searching
if (!prevSearchKey !== !searchKey) scrollToItem(0, alignMode)
}, [prevSearchKey, searchKey, scrollToItem, alignMode])
@ -170,7 +178,7 @@ function LoadedFileExplorer({
])
const renderLabelText = useRenderLabelText(searchKey)
const goToCurrentItem = React.useCallback(() => {
const goToCurrentItem = useCallback(() => {
const targetPath = getCurrentPath()
if (targetPath) expandTo(targetPath)
}, [getCurrentPath, expandTo])
@ -178,14 +186,14 @@ function LoadedFileExplorer({
useOnLocationChange(goToCurrentItem)
useAfterRedirect(goToCurrentItem)
const [currentPath, setCurrentPath] = React.useState(() => getCurrentPath())
useAfterRedirect(React.useCallback(() => setCurrentPath(getCurrentPath()), [getCurrentPath]))
const [currentPath, setCurrentPath] = useState(() => getCurrentPath())
useAfterRedirect(useCallback(() => setCurrentPath(getCurrentPath()), [getCurrentPath]))
useInspector('CurrentPath', currentPath)
const ref = React.useRef<HTMLDivElement | null>(null)
const ref = useRef<HTMLDivElement | null>(null)
useFocusOnPendingTarget(
'files',
React.useCallback(() => ref.current?.focus(), []),
useCallback(() => ref.current?.focus(), []),
)
return (

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React, { useCallback } from 'react'
import { VisibleNodesGeneratorMethods } from './hooks/useVisibleNodesGeneratorMethods'
import { AlignMode } from './useVirtualScroll'
@ -6,7 +6,7 @@ export function useHandleNodeFocus(
{ focusNode }: VisibleNodesGeneratorMethods,
setAlignMode: (mode: AlignMode) => void,
) {
return React.useCallback(
return useCallback(
(event: React.FocusEvent<HTMLElement, Element>, node: TreeNode) => {
setAlignMode('lazy')
focusNode(node)

View file

@ -1,8 +1,8 @@
import * as React from 'react'
import { useCallback, useEffect, useRef } from 'react'
function useLatestValueRef<T>(value: T) {
const ref = React.useRef(value)
React.useEffect(() => {
const ref = useRef(value)
useEffect(() => {
ref.current = value
})
return ref
@ -11,5 +11,5 @@ export function useCallbackRef<Args extends AnyArray, R>(
callback: (...args: Args) => R,
): (...args: Args) => R {
const ref = useLatestValueRef(callback)
return React.useCallback((...args: Args) => ref.current(...args), [ref])
return useCallback((...args: Args) => ref.current(...args), [ref])
}

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React, { useCallback, useMemo, useRef, useState } from 'react'
import { useCallbackRef } from './useLatestValueRef'
function memoize<Args extends AnyArray, R>(
@ -29,14 +29,14 @@ export function useVirtualScroll<E extends HTMLElement>({
}) {
const totalHeight = totalAmount * rowHeight
const ref = React.useRef<E | null>(null) // TODO: compare DOM native event listener
const [scrollTop, setScrollTop] = React.useState(0)
const ref = useRef<E | null>(null) // TODO: compare DOM native event listener
const [scrollTop, setScrollTop] = useState(0)
const onScroll = React.useCallback((e: React.UIEvent<E, UIEvent>) => {
const onScroll = useCallback((e: React.UIEvent<E, UIEvent>) => {
setScrollTop(e.currentTarget.scrollTop)
}, [])
const [startRenderIndex, endRenderIndex] = React.useMemo(() => {
const [startRenderIndex, endRenderIndex] = useMemo(() => {
const viewportLastItemOverflow = viewportHeight % rowHeight
const visibleRowCount = (viewportHeight - viewportLastItemOverflow) / rowHeight
const inViewIndexFirst = (Math.min(scrollTop, totalHeight - viewportHeight) / rowHeight) >> 0
@ -46,14 +46,14 @@ export function useVirtualScroll<E extends HTMLElement>({
return [renderIndexFirst, renderIndexLast]
}, [scrollTop, viewportHeight, overScan, rowHeight, totalAmount, totalHeight])
const indexes = React.useMemo(() => {
const indexes = useMemo(() => {
const indexes: number[] = []
let i = startRenderIndex
while (i < endRenderIndex) indexes.push(i++)
return indexes
}, [startRenderIndex, endRenderIndex])
const mapStyles = React.useCallback(
const mapStyles = useCallback(
(row: number): React.CSSProperties => ({
position: 'absolute',
top: 0,
@ -63,9 +63,9 @@ export function useVirtualScroll<E extends HTMLElement>({
}),
[rowHeight],
)
const memoizedStyler = React.useMemo(() => memoize(mapStyles, row => row), [mapStyles])
const memoizedStyler = useMemo(() => memoize(mapStyles, row => row), [mapStyles])
const visibleRows: { row: number; style: React.CSSProperties }[] = React.useMemo(
const visibleRows: { row: number; style: React.CSSProperties }[] = useMemo(
() =>
indexes.map(row => ({
row,
@ -74,7 +74,7 @@ export function useVirtualScroll<E extends HTMLElement>({
[indexes, memoizedStyler],
)
const containerStyle: React.CSSProperties = React.useMemo(
const containerStyle: React.CSSProperties = useMemo(
() => ({
height: totalHeight,
position: 'relative',

View file

@ -1,11 +1,11 @@
import * as React from 'react'
import { useContext, useEffect } from 'react'
import { SidebarContext } from './SidebarContext'
export type FocusTarget = 'files' | 'search' | null
export function useFocusOnPendingTarget(target: FocusTarget, method: () => void) {
const { pendingFocusTarget } = React.useContext(SidebarContext)
React.useEffect(() => {
const { pendingFocusTarget } = useContext(SidebarContext)
useEffect(() => {
if (pendingFocusTarget.value === target) {
method()
pendingFocusTarget.onChange(null)

View file

@ -2,7 +2,7 @@ import { GearIcon, SyncIcon } from '@primer/octicons-react'
import { Link } from '@primer/react'
import { ReloadContext } from 'containers/ReloadContext'
import { VERSION } from 'env'
import * as React from 'react'
import React, { useContext } from 'react'
import { RoundIconButton } from './RoundIconButton'
import { wikiLinks } from './settings/SettingsBar'
@ -12,7 +12,7 @@ type Props = {
export function Footer(props: Props) {
const { toggleShowSettings } = props
const reload = React.useContext(ReloadContext)
const reload = useContext(ReloadContext)
return (
<div className={'gitako-footer'}>
<div className="gitako-footer-section">

View file

@ -2,7 +2,7 @@ import { SideBar } from 'components/SideBar'
import { ConfigsContextWrapper } from 'containers/ConfigsContext'
import { InspectorContextWrapper } from 'containers/Inspector'
import { ReloadContextWrapper } from 'containers/ReloadContext'
import * as React from 'react'
import React, { useMemo } from 'react'
import { StyleSheetManager } from 'styled-components'
import { insertMountPoint } from 'utils/DOMHelper'
import { ErrorBoundary } from '../containers/ErrorBoundary'
@ -12,7 +12,7 @@ import { RepoContextWrapper } from '../containers/RepoContext'
import { StateBarStateContextWrapper } from '../containers/SideBarState'
export function Gitako() {
const mountPoint = React.useMemo(() => insertMountPoint(), [])
const mountPoint = useMemo(() => insertMountPoint(), [])
return (
<StyleSheetManager target={mountPoint}>
<ReloadContextWrapper>

View file

@ -1,8 +1,8 @@
import * as React from 'react'
import React, { useMemo } from 'react'
import { getIsSupportedRegex } from './searchModes/regexMode'
export const Highlight = function Highlight({ text, match }: { text: string; match?: RegExp }) {
const $match = React.useMemo(
const $match = useMemo(
() =>
match instanceof RegExp
? match.flags.includes('g')
@ -12,7 +12,7 @@ export const Highlight = function Highlight({ text, match }: { text: string; mat
[match],
)
const chunks = React.useMemo(() => getChunks(text, $match), [text, $match])
const chunks = useMemo(() => getChunks(text, $match), [text, $match])
return <>{chunks.map(([type, text], key) => React.createElement(type, { key }, text))}</>
}

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React from 'react'
export function HighlightOnIndexes({ text, indexes = [] }: { text: string; indexes?: number[] }) {
return (

View file

@ -3,16 +3,15 @@ import {
ChevronRightIcon as ChevronRight,
ClockIcon as Clock,
CommentIcon as Comment,
DiffAddedIcon as DiffAdded,
DiffIcon as Diff,
DiffAddedIcon as DiffAdded,
DiffIgnoredIcon as DiffIgnored,
DiffModifiedIcon as DiffModified,
DiffRemovedIcon as DiffRemoved,
DiffRenamedIcon as DiffRenamed,
FileCodeIcon as FileCode,
FileIcon as File,
FileCodeIcon as FileCode,
FileMediaIcon as FileMedia,
FileSubmoduleIcon as Submodule,
FileZipIcon as FileZip,
GearIcon as Gear,
GrabberIcon as Grabber,
@ -22,10 +21,11 @@ import {
PinIcon as Pin,
ReplyIcon as Reply,
SearchIcon as Search,
FileSubmoduleIcon as Submodule,
TabIcon as Tab,
XIcon as X,
} from '@primer/octicons-react'
import * as React from 'react'
import React from 'react'
import { cx } from 'utils/cx'
const iconToComponentMap = {

View file

@ -1,5 +1,5 @@
import { Checkbox as PrimerCheckbox, CheckboxProps, FormControl } from '@primer/react'
import * as React from 'react'
import { CheckboxProps, FormControl, Checkbox as PrimerCheckbox } from '@primer/react'
import React from 'react'
export function Checkbox({
label,

View file

@ -1,5 +1,5 @@
import { FormControl, Select, SelectProps } from '@primer/react'
import * as React from 'react'
import React from 'react'
export type Option<T> = {
key: string

View file

@ -1,5 +1,5 @@
import { HourglassIcon } from '@primer/octicons-react'
import * as React from 'react'
import React from 'react'
type Props = {
text: React.ReactNode

View file

@ -2,11 +2,11 @@ import { GitBranchIcon } from '@primer/octicons-react'
import { Box, BranchName, Breadcrumbs, Text } from '@primer/react'
import { RepoContext } from 'containers/RepoContext'
import { platform } from 'platforms'
import * as React from 'react'
import React, { useContext } from 'react'
import { createAnchorClickHandler } from 'utils/createAnchorClickHandler'
export function MetaBar() {
const metaData = React.useContext(RepoContext)
const metaData = useContext(RepoContext)
if (!metaData) return null
const { userName, repoName, branchName } = metaData

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React from 'react'
import * as ReactDOM from 'react-dom'
type Props = {

View file

@ -1,6 +1,6 @@
import { GrabberIcon } from '@primer/octicons-react'
import { Icon } from 'components/Icon'
import * as React from 'react'
import React from 'react'
import { ResizeHandlerOptions, useResizeHandler } from '../utils/hooks/useResizeHandler'
import { Size2D } from './Size'

View file

@ -1,7 +1,7 @@
import { AlertIcon, SearchIcon, XIcon } from '@primer/octicons-react'
import { Popover, Text, TextInput, TextInputProps } from '@primer/react'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import React, { useCallback, useMemo, useRef } from 'react'
import { formatWithShortcut, isValidRegexpSource } from 'utils/general'
import { useFocusOnPendingTarget } from './FocusTarget'
import { SearchMode } from './searchModes'
@ -13,10 +13,10 @@ type Props = {
} & Required<Pick<TextInputProps, 'onFocus'>>
export function SearchBar({ onSearch, onFocus, value }: Props) {
const ref = React.useRef<HTMLInputElement | null>(null)
const ref = useRef<HTMLInputElement | null>(null)
useFocusOnPendingTarget(
'search',
React.useCallback(() => ref.current?.focus(), []),
useCallback(() => ref.current?.focus(), []),
)
const configs = useConfigs()
@ -27,7 +27,7 @@ export function SearchBar({ onSearch, onFocus, value }: Props) {
? 'Match file name with regular expression.'
: `Match file path sequence with plain input.`
const isInputValid = React.useMemo(
const isInputValid = useMemo(
() =>
({
regex: isValidRegexpSource(value),
@ -35,7 +35,7 @@ export function SearchBar({ onSearch, onFocus, value }: Props) {
}[searchMode]),
[value, searchMode],
)
const isSupportedRegex = React.useMemo(
const isSupportedRegex = useMemo(
() => !(searchMode === 'regex' && !getIsSupportedRegex(value)),
[value, searchMode],
)

View file

@ -7,7 +7,7 @@ import { Portal } from 'components/Portal'
import { ToggleShowButton } from 'components/ToggleShowButton'
import { useConfigs } from 'containers/ConfigsContext'
import { platform, platformName } from 'platforms'
import * as React from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { IIFC } from 'react-iifc'
import { useWindowSize } from 'react-use'
import { Config } from 'utils/config/helper'
@ -50,9 +50,9 @@ export function SideBar() {
const configContext = useConfigs()
const blockLeaveRef = React.useRef(false)
const blockLeaveRef = useRef(false)
const { sidebarToggleMode, shortcut, focusSearchInputShortcut } = configContext.value
const onResizeStateChange = React.useCallback((state: ResizeState) => {
const onResizeStateChange = useCallback((state: ResizeState) => {
blockLeaveRef.current = state === 'resizing'
}, [])
@ -61,7 +61,7 @@ export function SideBar() {
() => useWindowSize().height, // eslint-disable-line react-hooks/rules-of-hooks
)
const sidebarContextValue = React.useMemo(() => ({ pendingFocusTarget }), [pendingFocusTarget])
const sidebarContextValue = useMemo(() => ({ pendingFocusTarget }), [pendingFocusTarget])
const placement = configContext.value.sidebarPlacement
@ -146,15 +146,12 @@ export function SideBar() {
</div>
<IIFC>
{() => {
const [showSettings, setShowSettings] = React.useState(false)
const toggleShowSettings = React.useCallback(
() => setShowSettings(show => !show),
[],
)
const [showSettings, setShowSettings] = useState(false)
const toggleShowSettings = useCallback(() => setShowSettings(show => !show), [])
useOnShortcutPressed(
focusSearchInputShortcut,
React.useCallback(() => setShowSettings(false), []),
useCallback(() => setShowSettings(false), []),
)
return (
@ -201,19 +198,19 @@ function ToggleShowButtonWrapper({
}
function useFocusSidebarOnExpand(shouldExpand: boolean) {
React.useEffect(() => {
useEffect(() => {
// prevent keeping focus within Gitako
if (!shouldExpand) document.body.focus()
}, [shouldExpand])
}
function useMarkGitakoGlobalAttributes() {
React.useEffect(() => {
useEffect(() => {
const detach = DOMHelper.attachStickyGitakoPlatform()
DOMHelper.markGitakoPlatform()
return () => detach()
}, [])
React.useEffect(() => {
useEffect(() => {
const detach = DOMHelper.attachStickyGitakoReadyState()
DOMHelper.markGitakoReadyState(true)
return () => {
@ -224,8 +221,8 @@ function useMarkGitakoGlobalAttributes() {
}
function useLogoContainerElement() {
const [logoContainerElement, setLogoContainerElement] = React.useState<HTMLElement | null>(null)
React.useEffect(() => {
const [logoContainerElement, setLogoContainerElement] = useState<HTMLElement | null>(null)
useEffect(() => {
setLogoContainerElement(DOMHelper.insertLogoMountPoint())
}, [])
return logoContainerElement
@ -233,7 +230,7 @@ function useLogoContainerElement() {
function useUpdateBodyIndentOnStateUpdate(shouldExpand: boolean) {
const { sidebarToggleMode, sidebarPlacement } = useConfigs().value
React.useEffect(() => {
useEffect(() => {
if (!(sidebarToggleMode === 'persistent' && shouldExpand)) return
const detach = DOMHelper.attachStickyBodyIndent()
@ -257,7 +254,7 @@ const getDerivedExpansion = ({
function useGetDerivedExpansion() {
const { intelligentToggle, sidebarToggleMode } = useConfigs().value
return React.useCallback(
return useCallback(
() => getDerivedExpansion({ intelligentToggle, sidebarToggleMode }),
[intelligentToggle, sidebarToggleMode],
)
@ -266,7 +263,7 @@ function useGetDerivedExpansion() {
function useUpdateBodyIndentAfterRedirect(update: (shouldExpand: boolean) => void) {
const { intelligentToggle, sidebarToggleMode, sidebarPlacement } = useConfigs().value
useAfterRedirect(
React.useCallback(() => {
useCallback(() => {
// check and update expand state if pinned and auto-expand checked
if (sidebarToggleMode === 'persistent') {
const shouldExpand = getDerivedExpansion({ intelligentToggle, sidebarToggleMode })
@ -282,7 +279,7 @@ function useUpdateBodyIndentAfterRedirect(update: (shouldExpand: boolean) => voi
function useSaveExpandStateOnToggle(shouldExpand: boolean) {
const configContext = useConfigs()
const { intelligentToggle } = configContext.value
React.useEffect(() => {
useEffect(() => {
if (intelligentToggle !== null) configContext.onChange({ intelligentToggle: shouldExpand })
}, [shouldExpand, intelligentToggle]) // eslint-disable-line react-hooks/exhaustive-deps
}
@ -297,7 +294,7 @@ function useCollapseOnNoPermissionWhenTokenHasBeenSet(
intelligentToggle === null &&
!!accessToken &&
state === 'error-due-to-auth'
React.useEffect(() => {
useEffect(() => {
if (hideSidebarOnInvalidToken) setShowSideBar(false)
}, [hideSidebarOnInvalidToken, setShowSideBar])
}
@ -305,11 +302,8 @@ function useCollapseOnNoPermissionWhenTokenHasBeenSet(
function useShouldExpand() {
const getDerivedExpansion = useGetDerivedExpansion()
const error = useLoadedContext(SideBarErrorContext).value
const [shouldExpand, setShouldExpand] = React.useState(getDerivedExpansion)
const toggleShowSideBar = React.useCallback(
() => setShouldExpand(show => !show),
[setShouldExpand],
)
const [shouldExpand, setShouldExpand] = useState(getDerivedExpansion)
const toggleShowSideBar = useCallback(() => setShouldExpand(show => !show), [setShouldExpand])
const $shouldExpand = error ? false : shouldExpand
@ -331,7 +325,7 @@ function useShowSidebarKeyboard(
useOnShortcutPressed(
config.shortcut,
React.useCallback(
useCallback(
e => {
DOMHelper.cancelEvent(e)
toggleShowSideBar()
@ -343,7 +337,7 @@ function useShowSidebarKeyboard(
useOnShortcutPressed(
config.focusSearchInputShortcut,
React.useCallback(
useCallback(
e => {
DOMHelper.cancelEvent(e)
if (!shouldExpand) setShouldExpand(true)

View file

@ -1,5 +1,5 @@
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react'
import { useDebounce, useLatest, useWindowSize } from 'react-use'
import { getDefaultConfigs } from 'utils/config/helper'
import * as DOMHelper from 'utils/DOMHelper'
@ -18,24 +18,24 @@ function useSidebarWidth() {
// width => --gitako-width // layout effect
// resize event => width
// resize event => --gitako-width // rAF
const [width, setWidth] = React.useState(configContext.value.sideBarWidth)
const [width, setWidth] = useState(configContext.value.sideBarWidth)
const { width: windowWidth } = useWindowSize()
React.useEffect(() => {
useEffect(() => {
const safeSize = getSafeWidth(width, windowWidth)
if (safeSize !== width) setWidth(safeSize)
}, [windowWidth, width])
useDebounce(() => configContext.onChange({ sideBarWidth: width }), 100, [width])
React.useLayoutEffect(() => DOMHelper.setGitakoWidthCSSVariable(width), [width])
useLayoutEffect(() => DOMHelper.setGitakoWidthCSSVariable(width), [width])
const widthRef = useLatest(width)
React.useEffect(() => {
useEffect(() => {
const detach = DOMHelper.attachStickyGitakoWidthCSSVariable(() => widthRef.current)
return () => detach()
}, [widthRef])
// Keep variable when directing from PR to repo home via meta bar
useAfterRedirect(React.useCallback(() => DOMHelper.setGitakoWidthCSSVariable(width), [width]))
useAfterRedirect(useCallback(() => DOMHelper.setGitakoWidthCSSVariable(width), [width]))
return [width, setWidth] as const
}
@ -45,7 +45,7 @@ export function SideBarResizeHandler({
}: Pick<ResizeHandlerOptions, 'onResizeStateChange'>) {
const [width, setWidth] = useSidebarWidth()
const { width: windowWidth } = useWindowSize()
const onResize = React.useMemo(() => {
const onResize = useMemo(() => {
let widthToApply: Size
let pending = false
return ([width]: Size2D) => {
@ -65,12 +65,9 @@ export function SideBarResizeHandler({
}
}, [windowWidth, setWidth])
const onResetSize = React.useCallback(
() => setWidth(getDefaultConfigs().sideBarWidth),
[setWidth],
)
const onResetSize = useCallback(() => setWidth(getDefaultConfigs().sideBarWidth), [setWidth])
const dummySize: Size2D = React.useMemo(() => [width, 0], [width])
const dummySize: Size2D = useMemo(() => [width, 0], [width])
const placement = useConfigs().value.sidebarPlacement

View file

@ -3,7 +3,7 @@ import iconURL from 'assets/icons/Gitako.png'
import { useConfigs } from 'containers/ConfigsContext'
import { SideBarErrorContext } from 'containers/ErrorContext'
import { ReloadContext } from 'containers/ReloadContext'
import * as React from 'react'
import React, { useContext, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { useDebounce, useWindowSize } from 'react-use'
import { cx } from 'utils/cx'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
@ -23,14 +23,14 @@ function getSafeDistance(y: number, height: number) {
}
export function ToggleShowButton({ className, onClick, onHover }: Props) {
const reload = React.useContext(ReloadContext)
const reload = useContext(ReloadContext)
const error = useLoadedContext(SideBarErrorContext).value
const ref = React.useRef<HTMLDivElement>(null)
const ref = useRef<HTMLDivElement>(null)
const config = useConfigs()
const [distance, setDistance] = React.useState(config.value.toggleButtonVerticalDistance)
const [distance, setDistance] = useState(config.value.toggleButtonVerticalDistance)
const { height } = useWindowSize()
React.useEffect(() => {
useEffect(() => {
// make sure it is inside viewport
const safeDistance = getSafeDistance(distance, height)
if (safeDistance !== distance) setDistance(safeDistance)
@ -44,7 +44,7 @@ export function ToggleShowButton({ className, onClick, onHover }: Props) {
)
// reposition on window height change, but ignores distance change
React.useLayoutEffect(() => {
useLayoutEffect(() => {
if (ref.current) {
ref.current.style.top = distance + 'px'
}

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React from 'react'
import { cx } from 'utils/cx'
import { hasUpperCase } from 'utils/general'
import { ModeShape } from '.'

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React from 'react'
import { cx } from 'utils/cx'
import { searchKeyToRegexp } from 'utils/general'
import { ModeShape } from '.'

View file

@ -5,7 +5,7 @@ import { SideBarStateContext } from 'containers/SideBarState'
import { platform } from 'platforms'
import { Gitea } from 'platforms/Gitea'
import { Gitee } from 'platforms/Gitee'
import * as React from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import { IIFC } from 'react-iifc'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { useStateIO } from 'utils/hooks/useStateIO'
@ -17,19 +17,19 @@ export function AccessTokenSettings() {
const configContext = useConfigs()
const { accessToken } = configContext.value
const hasAccessToken = Boolean(accessToken)
const [accessTokenInputValue, setAccessTokenInputValue] = React.useState('')
const [accessTokenInputValue, setAccessTokenInputValue] = useState('')
const useAccessTokenHint = useStateIO<React.ReactNode>('')
const focusInput = useStateIO(false)
const sidebarState = useLoadedContext(SideBarStateContext).value
const { value: accessTokenHint } = useAccessTokenHint
React.useEffect(() => {
useEffect(() => {
// clear input when access token updates
setAccessTokenInputValue('')
}, [accessToken])
const saveToken = React.useCallback(
const saveToken = useCallback(
async (
hint: typeof useAccessTokenHint.value = (
<span>
@ -73,16 +73,16 @@ export function AccessTokenSettings() {
) : hasAccessToken ? (
<IIFC>
{() => {
const [showConfirmButton, setShowConfirmButton] = React.useState(false)
const [showConfirmButton, setShowConfirmButton] = useState(false)
return (
<Box>
{showConfirmButton ? (
<IIFC>
{() => {
const [allowClear, setAllowClear] = React.useState(false)
const [allowClear, setAllowClear] = useState(false)
const waitForSeconds = 3
const timePast = useTimePast(1000, waitForSeconds * 1000)
React.useEffect(() => {
useEffect(() => {
const timeout = setTimeout(() => setAllowClear(true), waitForSeconds * 1000)
return () => clearTimeout(timeout)
}, [])
@ -168,8 +168,8 @@ export function AccessTokenSettings() {
}
function useTimePast(unit = 1000, max?: number) {
const [timePast, setTimePast] = React.useState(0)
React.useEffect(() => {
const [timePast, setTimePast] = useState(0)
useEffect(() => {
const checkInterval = (unit / 10) >> 0 // 10x check times for better accuracy
const start = Date.now()
let memoLastValue = 0

View file

@ -1,6 +1,6 @@
import { wikiLinks } from 'components/settings/SettingsBar'
import { SimpleConfigFieldCheckbox } from 'components/settings/SimpleConfigField/Checkbox'
import * as React from 'react'
import React from 'react'
import { Config } from 'utils/config/helper'
import { Option } from '../Inputs/SelectInput'
import { SettingsSection } from './SettingsSection'

View file

@ -1,5 +1,5 @@
import { Box, Button, FormControl, TextInput } from '@primer/react'
import * as React from 'react'
import React, { useMemo } from 'react'
import { useUpdateEffect } from 'react-use'
import { cancelEvent } from 'utils/DOMHelper'
import { friendlyFormatShortcut, noop } from 'utils/general'
@ -15,7 +15,7 @@ export function KeyboardShortcutSetting({ label, value, onChange }: Props) {
const $shortcut = useStateIO(value)
useUpdateEffect(() => $shortcut.onChange(value), [value])
const id = React.useMemo(() => Math.random() + '', [])
const id = useMemo(() => Math.random() + '', [])
return (
<FormControl>

View file

@ -5,7 +5,7 @@ import { RoundIconButton } from 'components/RoundIconButton'
import { useConfigs } from 'containers/ConfigsContext'
import { platform } from 'platforms'
import { GitHub } from 'platforms/GitHub'
import * as React from 'react'
import React from 'react'
import { useUpdateEffect } from 'react-use'
import { useStateIO } from 'utils/hooks/useStateIO'
import { AccessTokenSettings } from './AccessTokenSettings'

View file

@ -1,5 +1,5 @@
import { Box } from '@primer/react'
import * as React from 'react'
import React from 'react'
type Props = {
title?: React.ReactNode

View file

@ -1,6 +1,6 @@
import { SimpleConfigFieldCheckbox } from 'components/settings/SimpleConfigField/Checkbox'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import React from 'react'
import { subIO } from 'utils/general'
import { KeyboardShortcutSetting } from './KeyboardShortcutSetting'
import { SettingsSection } from './SettingsSection'

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React from 'react'
import { ConfigKeys } from 'utils/config/helper'
import { SimpleConfigFieldProps, useSimpleConfigFieldIO } from '.'
import { Checkbox } from '../../Inputs/Checkbox'

View file

@ -1,6 +1,6 @@
import { InfoIcon, LinkExternalIcon } from '@primer/octicons-react'
import { Box } from '@primer/react'
import * as React from 'react'
import React from 'react'
import { ConfigKeys } from 'utils/config/helper'
import { SimpleConfigField } from '.'

View file

@ -1,5 +1,5 @@
import { SelectInput, SelectInputProps } from 'components/Inputs/SelectInput'
import * as React from 'react'
import React from 'react'
import { Config, ConfigKeys } from 'utils/config/helper'
import { SimpleConfigFieldProps, useSimpleConfigFieldIO } from '.'
import { FieldLabel } from './FieldLabel'

View file

@ -1,5 +1,5 @@
import { useConfigs } from 'containers/ConfigsContext'
import React from 'react'
import { useCallback, useMemo } from 'react'
import { Config, ConfigKeys } from 'utils/config/helper'
export type SimpleConfigField<Key extends ConfigKeys> = {
@ -26,8 +26,8 @@ export function useSimpleConfigFieldIO<Key extends ConfigKeys>(
const value = configContext.value[field.key]
return {
value: React.useMemo(() => (overwrite ? overwrite.value(value) : value), [overwrite, value]),
onChange: React.useCallback(
value: useMemo(() => (overwrite ? overwrite.value(value) : value), [overwrite, value]),
onChange: useCallback(
(newValue: Config[Key]) => {
configContext.onChange({ [field.key]: overwrite ? overwrite.onChange(newValue) : newValue })
},

View file

@ -1,5 +1,5 @@
import { PropsWithChildren } from 'common'
import * as React from 'react'
import React, { useCallback, useContext, useEffect, useState } from 'react'
import { Config, configHelper } from 'utils/config/helper'
type ContextShape = IO<Config, Partial<Config>>
@ -8,11 +8,11 @@ export type ConfigsContextShape = ContextShape
export const ConfigsContext = React.createContext<ContextShape | null>(null)
export function ConfigsContextWrapper(props: PropsWithChildren) {
const [configs, setConfigs] = React.useState<Config | null>(null)
React.useEffect(() => {
const [configs, setConfigs] = useState<Config | null>(null)
useEffect(() => {
configHelper.get().then(setConfigs)
}, [])
const onChange = React.useCallback(
const onChange = useCallback(
(updatedConfigs: Partial<Config>) => {
const mergedConfigs = { ...configs, ...updatedConfigs } as Config
configHelper.set(mergedConfigs)
@ -32,7 +32,7 @@ export const useConfigs = createUseNonNullContext(ConfigsContext)
function createUseNonNullContext<T>(theContext: React.Context<T | null>): () => T {
return () => {
const context = React.useContext(theContext)
const context = useContext(theContext)
if (context === null) throw new Error(`Empty context`)
return context
}

View file

@ -1,6 +1,6 @@
import { raiseError } from 'analytics'
import { PropsWithChildren } from 'common'
import * as React from 'react'
import React from 'react'
export class ErrorBoundary extends React.PureComponent<PropsWithChildren> {
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {

View file

@ -1,6 +1,6 @@
import { PropsWithChildren } from 'common'
import { useInspector } from 'containers/Inspector'
import * as React from 'react'
import React from 'react'
import { useStateIO } from 'utils/hooks/useStateIO'
export type SideBarErrorContextShape = IO<string | null>

View file

@ -1,10 +1,10 @@
import { PropsWithChildren, ReactIO } from 'common'
import { IN_PRODUCTION_MODE } from 'env'
import * as React from 'react'
import React, { useContext, useEffect } from 'react'
import { Config } from 'utils/config/helper'
import { noop } from 'utils/general'
import { useStateIO } from 'utils/hooks/useStateIO'
import { useConfigs } from './ConfigsContext'
import { Config } from 'utils/config/helper'
export type InspectorContextShape = ReactIO<JSONObject>
@ -66,8 +66,8 @@ export const InspectorContextWrapper = IN_PRODUCTION_MODE
export const useInspector = IN_PRODUCTION_MODE
? noop
: function useInspector(key: string, value: JSONValue) {
const $ = React.useContext(InspectorContext)
React.useEffect(() => {
const $ = useContext(InspectorContext)
useEffect(() => {
$?.onChange(prev => ({ ...prev, [key]: value }))
}, [key, value]) // eslint-disable-line react-hooks/exhaustive-deps
}

View file

@ -1,7 +1,7 @@
import { PropsWithChildren } from 'common'
import { useConfigs } from 'containers/ConfigsContext'
import { platform } from 'platforms'
import * as React from 'react'
import React, { useEffect, useRef } from 'react'
import { parseURLSearch, run } from 'utils/general'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { useStateIO } from 'utils/hooks/useStateIO'
@ -15,8 +15,8 @@ export function OAuthWrapper({ children }: PropsWithChildren) {
const running = useGetAccessToken()
const $state = useLoadedContext(SideBarStateContext)
const needGetAccessTokenRef = React.useRef(running)
React.useEffect(() => {
const needGetAccessTokenRef = useRef(running)
useEffect(() => {
if (needGetAccessTokenRef.current) {
$state.onChange(running ? 'getting-access-token' : 'after-getting-access-token')
}
@ -31,7 +31,7 @@ function useGetAccessToken() {
const $block = useStateIO(() => Boolean(getCodeSearchParam()))
const configContext = useConfigs()
const { accessToken } = configContext.value
React.useEffect(() => {
useEffect(() => {
run(async function () {
const code = getCodeSearchParam()
if (code && !accessToken) {

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React from 'react'
export type PortalContextShape = string | null

View file

@ -1,5 +1,5 @@
import { PropsWithChildren } from 'common'
import * as React from 'react'
import React, { useCallback, useState } from 'react'
import { noop } from 'utils/general'
export type ReloadContextShape = () => void
@ -7,8 +7,8 @@ export type ReloadContextShape = () => void
export const ReloadContext = React.createContext<ReloadContextShape>(noop)
export function ReloadContextWrapper({ children }: PropsWithChildren) {
const [key, setKey] = React.useState(0)
const reload = React.useCallback(() => setKey(key => key + 1), [])
const [key, setKey] = useState(0)
const reload = useCallback(() => setKey(key => key + 1), [])
return (
<ReloadContext.Provider key={key} value={reload}>

View file

@ -1,15 +1,15 @@
import { PropsWithChildren } from 'common'
import { useConfigs } from 'containers/ConfigsContext'
import { platform } from 'platforms'
import * as React from 'react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useAbortableEffect } from 'utils/hooks/useAbortableEffect'
import { useEffectOnSerializableUpdates } from 'utils/hooks/useEffectOnSerializableUpdates'
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
import { useHandleNetworkError } from 'utils/hooks/useHandleNetworkError'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { useStateIO } from 'utils/hooks/useStateIO'
import { SideBarStateContext } from './SideBarState'
import { useInspector } from './Inspector'
import { SideBarStateContext } from './SideBarState'
export const RepoContext = React.createContext<MetaData | null>(null)
@ -18,7 +18,7 @@ export function RepoContextWrapper({ children }: PropsWithChildren) {
const metaData = useMetaData(partialMetaData)
useInspector(
'RepoContext',
React.useMemo(
useMemo(
() => ({
partialMetaData,
metaData,
@ -54,11 +54,11 @@ function usePartialMetaData(): PartialMetaData | null {
// sync along URL and DOM
const $partialMetaData = useStateIO(isGettingAccessToken ? null : resolvePartialMetaData)
const $committedPartialMetaData = useStateIO($partialMetaData.value)
const setPartialMetaData = React.useCallback(
const setPartialMetaData = useCallback(
() => $partialMetaData.onChange(resolvePartialMetaData()),
[], // eslint-disable-line react-hooks/exhaustive-deps
)
React.useEffect(() => {
useEffect(() => {
if (!isGettingAccessToken) setPartialMetaData()
}, [isGettingAccessToken, setPartialMetaData])
useAfterRedirect(setPartialMetaData)
@ -67,7 +67,7 @@ function usePartialMetaData(): PartialMetaData | null {
JSON.stringify,
$committedPartialMetaData.onChange,
)
React.useEffect(() => {
useEffect(() => {
if (!$partialMetaData.value && !isGettingAccessToken) {
$state.onChange('disabled')
}
@ -76,12 +76,12 @@ function usePartialMetaData(): PartialMetaData | null {
}
function useMetaData(partialMetaData: PartialMetaData | null) {
const [metaData, changeMetaData] = React.useState<MetaData | null>(null)
const [metaData, changeMetaData] = useState<MetaData | null>(null)
const changeLoadedState = useLoadedContext(SideBarStateContext).onChange
const handleNetworkError = useHandleNetworkError()
const { accessToken } = useConfigs().value
const loadRepoMetaData = React.useCallback(
const loadRepoMetaData = useCallback(
async function* loadRepoMetaData() {
if (!partialMetaData) return
@ -115,7 +115,7 @@ function useMetaData(partialMetaData: PartialMetaData | null) {
)
useAbortableEffect(
React.useCallback(
useCallback(
() => ({
getAsyncGenerator: loadRepoMetaData,
cancel: () => {

View file

@ -1,5 +1,5 @@
import { PropsWithChildren } from 'common'
import * as React from 'react'
import React, { useMemo } from 'react'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { useStateIO } from 'utils/hooks/useStateIO'
import { SideBarErrorContext } from './ErrorContext'
@ -26,7 +26,7 @@ export function StateBarStateContextWrapper({ children }: PropsWithChildren) {
const $state = useStateIO<SideBarState>('disabled')
useInspector('SideBarStateContext', $state.value)
const error = useLoadedContext(SideBarErrorContext).value
const $$state: IO<SideBarState> = React.useMemo(
const $$state: IO<SideBarState> = useMemo(
() =>
error && $state.value !== 'error'
? {

View file

@ -2,7 +2,7 @@ import primitives from '@primer/primitives'
import { BaseStyles, ThemeProvider } from '@primer/react'
import theme from '@primer/react/lib-esm/theme'
import { PropsWithChildren } from 'common'
import * as React from 'react'
import React, { useEffect, useState } from 'react'
// Temporary color fix for out-of-date embedded @primer/primitives in @primer/react
// The `*_tritanopia` themes are actually not bundled within @primer/react@35.2.2
@ -17,7 +17,7 @@ const fixedTheme = {
}
const validColorSchemes = Object.keys(fixedTheme.colorSchemes) as EnumString<
keyof typeof primitives['colors']
keyof (typeof primitives)['colors']
>[]
const colorModeMap: Record<string, 'night' | 'day'> = {
@ -39,8 +39,8 @@ const getPreferenceFromDOM = () => {
}
function useThemePreference() {
const [prefer, setPrefer] = React.useState(getPreferenceFromDOM)
React.useEffect(() => {
const [prefer, setPrefer] = useState(getPreferenceFromDOM)
useEffect(() => {
const match = window.matchMedia('(prefers-color-scheme: dark)')
const update = () => setPrefer(getPreferenceFromDOM)
match.addEventListener('change', update)

View file

@ -1,5 +1,5 @@
import { Gitako } from 'components/Gitako'
import * as React from 'react'
import React, { useCallback } from 'react'
import { createRoot } from 'react-dom/client'
import { insertMountPoint, insertSideBarMountPoint } from 'utils/DOMHelper'
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
@ -15,7 +15,7 @@ async function init() {
await injectStyles(browser.runtime.getURL('content.css'))
const mountPoint = insertSideBarMountPoint()
const MountPointWatcher = () => {
useAfterRedirect(React.useCallback(() => insertMountPoint(() => mountPoint), []))
useAfterRedirect(useCallback(() => insertMountPoint(() => mountPoint), []))
return null
}

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { cx } from 'utils/cx'
import { copyElementContent } from 'utils/DOMHelper'
import { getCodeElement } from './DOMHelper'
@ -13,8 +13,8 @@ const contents = {
}
export function CopyFileButton() {
const [content, setContent] = React.useState(contents.normal)
React.useEffect(() => {
const [content, setContent] = useState(contents.normal)
useEffect(() => {
if (content !== contents.normal) {
const timer = setTimeout(() => {
setContent(contents.normal)
@ -23,8 +23,8 @@ export function CopyFileButton() {
}
}, [content])
const elementRef = React.useRef<HTMLAnchorElement | null>(null)
React.useEffect(() => {
const elementRef = useRef<HTMLAnchorElement | null>(null)
useEffect(() => {
// Temporary fix:
// React moved root node of event delegation since v17
// onClick on <a /> won't work when rendered with `renderReact`

View file

@ -1,6 +1,6 @@
import { raiseError } from 'analytics'
import { Clippy, ClippyClassName } from 'components/Clippy'
import * as React from 'react'
import React from 'react'
import * as s from 'superstruct'
import { $ } from 'utils/$'
import { formatClass, parseIntFromElement } from 'utils/DOMHelper'

View file

@ -1,16 +1,16 @@
import { platform } from 'platforms'
import * as React from 'react'
import { useCallback, useEffect } from 'react'
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
import * as DOMHelper from '../DOMHelper'
import { GitHub } from '../index'
export function useGitHubAttachCopySnippetButton(copySnippetButton: boolean) {
const attachCopySnippetButton = React.useCallback(
const attachCopySnippetButton = useCallback(
function attachCopySnippetButton() {
if (platform === GitHub && copySnippetButton) DOMHelper.attachCopySnippet()
},
[copySnippetButton],
)
React.useEffect(attachCopySnippetButton, [attachCopySnippetButton])
useEffect(attachCopySnippetButton, [attachCopySnippetButton])
useAfterRedirect(attachCopySnippetButton)
}

View file

@ -1,6 +1,6 @@
import { raiseError } from 'analytics'
import { Clippy, ClippyClassName } from 'components/Clippy'
import * as React from 'react'
import React from 'react'
import { $ } from 'utils/$'
import { formatClass } from 'utils/DOMHelper'
import { renderReact } from 'utils/general'

View file

@ -1,7 +1,7 @@
import { GITEE_OAUTH } from 'env'
import { Base64 } from 'js-base64'
import { errors, platform } from 'platforms'
import * as React from 'react'
import { useCallback, useEffect } from 'react'
import { resolveGitModules } from 'utils/gitSubmodule'
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
import { useProgressBar } from 'utils/hooks/useProgressBar'
@ -187,12 +187,12 @@ export const Gitee: Platform = {
}
export function useGiteeAttachCopySnippetButton(copySnippetButton: boolean) {
const attachCopySnippetButton = React.useCallback(
const attachCopySnippetButton = useCallback(
function attachCopySnippetButton() {
if (platform === Gitee && copySnippetButton) DOMHelper.attachCopySnippet()
},
[copySnippetButton],
)
React.useEffect(attachCopySnippetButton, [attachCopySnippetButton])
useEffect(attachCopySnippetButton, [attachCopySnippetButton])
useAfterRedirect(attachCopySnippetButton)
}

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { useState } from 'react'
export function useConditionalHook<T>(condition: () => boolean, hook: () => T) {
const [use] = React.useState(condition)
const [use] = useState(condition)
if (use) return hook()
}

View file

@ -1,10 +1,10 @@
import * as React from 'react'
import { useEffect, useMemo } from 'react'
export function useEffectOnSerializableUpdates<T>(
value: T,
serialize: (value: T) => string,
onChange: (value: T) => void,
) {
const serialized = React.useMemo(() => serialize(value), [value, serialize])
React.useEffect(() => onChange(value), [onChange, serialized]) // eslint-disable-line react-hooks/exhaustive-deps
const serialized = useMemo(() => serialize(value), [value, serialize])
useEffect(() => onChange(value), [onChange, serialized]) // eslint-disable-line react-hooks/exhaustive-deps
}

View file

@ -1,13 +1,13 @@
import * as React from 'react'
import { useEffect, useRef, useState } from 'react'
import * as features from 'utils/features'
import { Size2D } from '../../components/Size'
export function useElementSize<E extends HTMLElement>() {
const ref = React.useRef<E | null>(null)
const ref = useRef<E | null>(null)
const [size, setSize] = React.useState<Size2D>([0, 0])
const [size, setSize] = useState<Size2D>([0, 0])
React.useEffect(() => {
useEffect(() => {
if (ref.current) {
if (features.resize) {
const observer = new window.ResizeObserver(entries => {

View file

@ -1,6 +1,6 @@
import { useConfigs } from 'containers/ConfigsContext'
import { platform } from 'platforms'
import * as React from 'react'
import { useCallback, useEffect, useRef } from 'react'
import { useEvent, useInterval } from 'react-use'
import { run } from 'utils/general'
@ -27,7 +27,7 @@ const config: import('pjax-api').Config = {
export function usePJAXAPI() {
const { pjaxMode } = useConfigs().value
// make history travel work
React.useEffect(() => {
useEffect(() => {
if (pjaxMode === 'pjax-api') {
run(async () => {
const { Pjax } = await import('pjax-api')
@ -52,8 +52,8 @@ export const loadWithFastRedirect = (url: string, element: HTMLElement) => {
}
export function useAfterRedirect(callback: () => void) {
const latestHref = React.useRef(location.href)
const raceCallback = React.useCallback(() => {
const latestHref = useRef(location.href)
const raceCallback = useCallback(() => {
const { href } = location
if (latestHref.current !== href) {
latestHref.current = href

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import React, { useContext } from 'react'
export function useLoadedContext<T>(context: React.Context<T | null>): T {
const ctx = React.useContext(context)
const ctx = useContext(context)
if (ctx === null) throw new Error(`Context not loaded`)
return ctx
}

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React, { useEffect } from 'react'
import { useLocation } from 'react-use'
export function useOnLocationChange(
@ -6,5 +6,5 @@ export function useOnLocationChange(
extraDeps: React.DependencyList = [],
) {
const { href, pathname, search } = useLocation()
React.useEffect(callback, [href, pathname, search, callback, ...extraDeps])
useEffect(callback, [href, pathname, search, callback, ...extraDeps])
}

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import { useEffect } from 'react'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import * as keyHelper from 'utils/keyHelper'
import { SideBarStateContext } from '../../containers/SideBarState'
@ -9,7 +9,7 @@ export function useOnShortcutPressed(
) {
const state = useLoadedContext(SideBarStateContext).value
const isDisabled = state === 'disabled' || !shortcut
React.useEffect(
useEffect(
function attachKeyDown() {
if (isDisabled) return

View file

@ -1,5 +1,5 @@
import * as NProgress from 'nprogress'
import * as React from 'react'
import { useEffect } from 'react'
import { useEvent } from 'react-use'
const progressBar = {
@ -12,7 +12,7 @@ const progressBar = {
}
export function useProgressBar() {
React.useEffect(() => {
useEffect(() => {
NProgress.configure({ showSpinner: false })
}, [])
useEvent('pjax:fetch', progressBar.mount, window)

View file

@ -1,4 +1,4 @@
import * as React from 'react'
import React, { useCallback, useEffect, useRef } from 'react'
import { Size2D } from '../../components/Size'
export type ResizeState = 'idle' | 'resizing'
@ -20,21 +20,21 @@ export function useResizeHandler(
direction = 'right',
}: ResizeHandlerOptions = {},
) {
const pointerDown = React.useRef(false)
const pointerMoved = React.useRef(false)
const initialSizeRef = React.useRef([0, 0])
const baseSize = React.useRef(size)
const latestPropSize = React.useRef(size)
const pointerDown = useRef(false)
const pointerMoved = useRef(false)
const initialSizeRef = useRef([0, 0])
const baseSize = useRef(size)
const latestPropSize = useRef(size)
const fix = {
left: -1,
right: 1,
}[direction]
React.useEffect(() => {
useEffect(() => {
latestPropSize.current = size
}, [size])
React.useEffect(() => {
useEffect(() => {
const onPointerMove = ({ clientX, clientY }: PointerEvent) => {
if (!pointerDown.current) return
const [x0, y0] = initialSizeRef.current
@ -48,7 +48,7 @@ export function useResizeHandler(
return () => window.removeEventListener('pointermove', onPointerMove)
}, [onResize, distanceTolerance, fix])
React.useEffect(() => {
useEffect(() => {
const onPointerUp = (e: PointerEvent) => {
if (pointerDown.current) {
pointerDown.current = false
@ -64,7 +64,7 @@ export function useResizeHandler(
return () => window.removeEventListener('pointerup', onPointerUp)
}, [onClick, onResizeStateChange])
const onPointerDown = React.useCallback(
const onPointerDown = useCallback(
(e: React.PointerEvent) => {
e.preventDefault() // Prevent unexpected selection when dragging in Safari
const { clientX, clientY } = e

View file

@ -1,9 +1,9 @@
import * as React from 'react'
import React, { useMemo, useState } from 'react'
export function useStateIO<S>(initialState: S | (() => S)): {
value: S
onChange: React.Dispatch<React.SetStateAction<S>>
} {
const [value, onChange] = React.useState(initialState)
return React.useMemo(() => ({ value, onChange }), [value])
const [value, onChange] = useState(initialState)
return useMemo(() => ({ value, onChange }), [value])
}

View file

@ -1,9 +1,9 @@
import { IN_PRODUCTION_MODE } from 'env'
import * as React from 'react'
import { useEffect, useRef } from 'react'
export function useUpdateReason<P extends Record<string, unknown>>(props: P) {
const lastPropsRef = React.useRef<P>(props)
React.useEffect(() => {
const lastPropsRef = useRef<P>(props)
useEffect(() => {
if (IN_PRODUCTION_MODE) return
const output: ([string, keyof P, P[keyof P]] | [string, keyof P, P[keyof P], P[keyof P]])[] = []
for (const key of Object.keys(props)) {