/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-devtools-shared/src/devtools/views/Profiler/HookChangeSummary.js
209 строк
6 KB
Jan Kassens
Update Flow to 0.266 (#34271)
22 авг 2025, 22:46
Не верифицирован
22 авг 2025, 22:46
05addfc
Код
Авторство
О чём код?
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import * as React from 'react'; import { useContext, useMemo, useCallback, memo, useState, useEffect, } from 'react'; import styles from './HookChangeSummary.css'; import ButtonIcon from '../ButtonIcon'; import {InspectedElementContext} from '../Components/InspectedElementContext'; import {StoreContext} from '../context'; import {getAlreadyLoadedHookNames} from 'react-devtools-shared/src/hookNamesCache'; import {getHookSourceLocationKey} from 'react-devtools-shared/src/hookSourceLocation'; import Toggle from '../Toggle'; import type {HooksNode} from 'react-debug-tools/src/ReactDebugHooks'; import type {ChangeDescription} from './types'; // $FlowFixMe: Flow doesn't know about Intl.ListFormat const hookListFormatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction', }); type HookProps = { hook: HooksNode, hookNames: Map<string, string> | null, }; const Hook: component(...props: HookProps) = memo( ({hook, hookNames}: HookProps) => { const hookSource = hook.hookSource; const hookName = useMemo(() => { if (!hookSource || !hookNames) return null; const key = getHookSourceLocationKey(hookSource); return hookNames.get(key) || null; }, [hookSource, hookNames]); return ( <ul className={styles.Hook}> <li> {hook.id !== null && ( <span className={styles.PrimitiveHookNumber}> {String(hook.id + 1)} </span> )} <span className={ hook.id !== null ? styles.PrimitiveHookName : styles.Name }> {hook.name} {hookName && <span className={styles.HookName}>({hookName})</span>} </span> {hook.subHooks?.map((subHook, index) => ( <Hook key={hook.id} hook={subHook} hookNames={hookNames} /> ))} </li> </ul> ); }, ); const shouldKeepHook = ( hook: HooksNode, hooksArray: Array<number>, ): boolean => { if (hook.id !== null && hooksArray.includes(hook.id)) { return true; } const subHooks = hook.subHooks; if (subHooks == null) { return false; } return subHooks.some(subHook => shouldKeepHook(subHook, hooksArray)); }; const filterHooks = ( hook: HooksNode, hooksArray: Array<number>, ): HooksNode | null => { if (!shouldKeepHook(hook, hooksArray)) { return null; } const subHooks = hook.subHooks; if (subHooks == null) { return hook; } const filteredSubHooks = subHooks .map(subHook => filterHooks(subHook, hooksArray)) .filter(Boolean); return filteredSubHooks.length > 0 ? {...hook, subHooks: filteredSubHooks} : hook; }; type Props = {| fiberID: number, hooks: ChangeDescription['hooks'], state: ChangeDescription['state'], displayMode?: 'detailed' | 'compact', |}; const HookChangeSummary: component(...props: Props) = memo( ({hooks, fiberID, state, displayMode = 'detailed'}: Props) => { const {parseHookNames, toggleParseHookNames, inspectedElement} = useContext( InspectedElementContext, ); const store = useContext(StoreContext); const [parseHookNamesOptimistic, setParseHookNamesOptimistic] = useState<boolean>(parseHookNames); useEffect(() => { setParseHookNamesOptimistic(parseHookNames); }, [inspectedElement?.id, parseHookNames]); const handleOnChange = useCallback(() => { setParseHookNamesOptimistic(!parseHookNames); toggleParseHookNames(); }, [toggleParseHookNames, parseHookNames]); const element = fiberID !== null ? store.getElementByID(fiberID) : null; const hookNames = element != null ? getAlreadyLoadedHookNames(element) : null; const filteredHooks = useMemo(() => { if (!hooks || !inspectedElement?.hooks) return null; return inspectedElement.hooks .map(hook => filterHooks(hook, hooks)) .filter(Boolean); }, [inspectedElement?.hooks, hooks]); const hookParsingFailed = parseHookNames && hookNames === null; if (!hooks?.length) { return <span>No hooks changed</span>; } if ( inspectedElement?.id !== element?.id || filteredHooks?.length !== hooks.length || displayMode === 'compact' ) { const hookIds = hooks.map(hookId => String(hookId + 1)); const hookWord = hookIds.length === 1 ? '• Hook' : '• Hooks'; return ( <span> {hookWord} {hookListFormatter.format(hookIds)} changed </span> ); } let toggleTitle: string; if (hookParsingFailed) { toggleTitle = 'Hook parsing failed'; } else if (parseHookNamesOptimistic) { toggleTitle = 'Parsing hook names ...'; } else { toggleTitle = 'Parse hook names (may be slow)'; } if (filteredHooks == null) { return null; } return ( <div> {filteredHooks.length > 1 ? '• Hooks changed:' : '• Hook changed:'} {(!parseHookNames || hookParsingFailed) && ( <Toggle className={ hookParsingFailed ? styles.ToggleError : styles.LoadHookNamesToggle } isChecked={parseHookNamesOptimistic} isDisabled={parseHookNamesOptimistic || hookParsingFailed} onChange={handleOnChange} title={toggleTitle}> <ButtonIcon type="parse-hook-names" /> </Toggle> )} {filteredHooks.map(hook => ( <Hook key={`${inspectedElement?.id ?? 'unknown'}-${hook.id}`} hook={hook} hookNames={hookNames} /> ))} </div> ); }, ); export default HookChangeSummary;