From ecf32939858caa73501a0ec652c2ef31c69f1407 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Wed, 27 Apr 2022 01:10:18 -0700 Subject: [PATCH] Back out "Remove AsyncStorage from rn-tester and fix InternalSettings Example" Summary: This diff breaks the RNTester app at runtime and it prevents us from cutting RN 0.69. Original commit changeset: a879787d8683 Original Phabricator Diff: D35435562 (https://github.com/facebook/react-native/commit/f4c4f446e4365c6cf7adf5db21d3d746b38aa3e2) ## Changelog [General][Fixed] - Revert change which breaks RNTester Reviewed By: cortinico, kacieb Differential Revision: D35936298 fbshipit-source-id: c4ec92002fde9c1de691bcda17ebc5d0747914f6 --- packages/rn-tester/js/RNTesterAppShared.js | 113 ++++++++++-------- .../js/components/RNTesterSettingSwitchRow.js | 59 ++++++--- .../NativeAnimationsExample.js | 107 +++++++++++------ packages/rn-tester/js/types/RNTesterTypes.js | 9 +- .../js/utils/RNTesterJsStallsContext.js | 35 ------ .../js/utils/RNTesterJsStallsReducer.js | 56 --------- ...TesterNavReducer.js => RNTesterReducer.js} | 22 ++-- .../js/utils/RNTesterStatePersister.js | 82 +++++++++++++ .../rn-tester/js/utils/testerStateUtils.js | 39 ++++-- .../js/utils/useAsyncStorageReducer.js | 33 +++++ .../rn-tester/js/utils/useJsStallsReducer.js | 90 -------------- 11 files changed, 322 insertions(+), 323 deletions(-) delete mode 100644 packages/rn-tester/js/utils/RNTesterJsStallsContext.js delete mode 100644 packages/rn-tester/js/utils/RNTesterJsStallsReducer.js rename packages/rn-tester/js/utils/{RNTesterNavReducer.js => RNTesterReducer.js} (84%) create mode 100644 packages/rn-tester/js/utils/RNTesterStatePersister.js create mode 100644 packages/rn-tester/js/utils/useAsyncStorageReducer.js delete mode 100644 packages/rn-tester/js/utils/useJsStallsReducer.js diff --git a/packages/rn-tester/js/RNTesterAppShared.js b/packages/rn-tester/js/RNTesterAppShared.js index e94999b35ac..fc35f5c89f5 100644 --- a/packages/rn-tester/js/RNTesterAppShared.js +++ b/packages/rn-tester/js/RNTesterAppShared.js @@ -23,29 +23,29 @@ import RNTesterNavBar, {navBarHeight} from './components/RNTesterNavbar'; import RNTesterList from './utils/RNTesterList'; import { Screens, - initialNavState, + initialState, getExamplesListWithBookmarksAndRecentlyUsed, + getInitialStateFromAsyncStorage, } from './utils/testerStateUtils'; -import { - RNTesterNavReducer, - RNTesterNavActionsType, -} from './utils/RNTesterNavReducer'; +import {useAsyncStorageReducer} from './utils/useAsyncStorageReducer'; +import {RNTesterReducer, RNTesterActionsType} from './utils/RNTesterReducer'; import {RNTesterThemeContext, themes} from './components/RNTesterTheme'; import RNTTitleBar from './components/RNTTitleBar'; import {RNTesterEmptyBookmarksState} from './components/RNTesterEmptyBookmarksState'; -import {RNTesterJsStallsProvider} from './utils/RNTesterJsStallsContext'; -// RNTester App currently uses in memory storage for storing navigation state +const APP_STATE_KEY = 'RNTesterAppState.v3'; + +// RNTester App currently uses AsyncStorage from react-native for storing navigation state // and bookmark items. -// TODO: Identify/implement solution for async device storage. +// TODO: Vendor AsyncStorage or create our own. LogBox.ignoreLogs([/AsyncStorage has been extracted from react-native/]); const RNTesterApp = (): React.Node => { - const [state, dispatch] = React.useReducer( - RNTesterNavReducer, - initialNavState, + const [state, dispatch] = useAsyncStorageReducer( + RNTesterReducer, + initialState, + APP_STATE_KEY, ); - const colorScheme = useColorScheme(); const { @@ -57,6 +57,17 @@ const RNTesterApp = (): React.Node => { recentlyUsed, } = state; + React.useEffect(() => { + getInitialStateFromAsyncStorage(APP_STATE_KEY).then( + initialStateFromStorage => { + dispatch({ + type: RNTesterActionsType.INIT_FROM_STORAGE, + data: initialStateFromStorage, + }); + }, + ); + }, [dispatch]); + const examplesList = React.useMemo( () => getExamplesListWithBookmarksAndRecentlyUsed({bookmarks, recentlyUsed}), @@ -65,7 +76,7 @@ const RNTesterApp = (): React.Node => { const handleBackPress = React.useCallback(() => { if (activeModuleKey != null) { - dispatch({type: RNTesterNavActionsType.BACK_BUTTON_PRESS}); + dispatch({type: RNTesterActionsType.BACK_BUTTON_PRESS}); } }, [dispatch, activeModuleKey]); @@ -92,7 +103,7 @@ const RNTesterApp = (): React.Node => { const handleModuleCardPress = React.useCallback( ({exampleType, key, title}) => { dispatch({ - type: RNTesterNavActionsType.MODULE_CARD_PRESS, + type: RNTesterActionsType.MODULE_CARD_PRESS, data: {exampleType, key, title}, }); }, @@ -102,7 +113,7 @@ const RNTesterApp = (): React.Node => { const handleModuleExampleCardPress = React.useCallback( exampleName => { dispatch({ - type: RNTesterNavActionsType.EXAMPLE_CARD_PRESS, + type: RNTesterActionsType.EXAMPLE_CARD_PRESS, data: {key: exampleName}, }); }, @@ -112,7 +123,7 @@ const RNTesterApp = (): React.Node => { const toggleBookmark = React.useCallback( ({exampleType, key}) => { dispatch({ - type: RNTesterNavActionsType.BOOKMARK_PRESS, + type: RNTesterActionsType.BOOKMARK_PRESS, data: {exampleType, key}, }); }, @@ -122,7 +133,7 @@ const RNTesterApp = (): React.Node => { const handleNavBarPress = React.useCallback( args => { dispatch({ - type: RNTesterNavActionsType.NAVBAR_PRESS, + type: RNTesterActionsType.NAVBAR_PRESS, data: {screen: args.screen}, }); }, @@ -159,42 +170,40 @@ const RNTesterApp = (): React.Node => { return ( - - - - {activeModule != null ? ( - - ) : screen === Screens.BOOKMARKS && - examplesList.bookmarks.length === 0 ? ( - - ) : ( - - )} - - - + + {activeModule != null ? ( + - - + ) : screen === Screens.BOOKMARKS && + examplesList.bookmarks.length === 0 ? ( + + ) : ( + + )} + + + + ); }; diff --git a/packages/rn-tester/js/components/RNTesterSettingSwitchRow.js b/packages/rn-tester/js/components/RNTesterSettingSwitchRow.js index 68bce4a81e3..5b84fe71acb 100644 --- a/packages/rn-tester/js/components/RNTesterSettingSwitchRow.js +++ b/packages/rn-tester/js/components/RNTesterSettingSwitchRow.js @@ -8,10 +8,38 @@ * @flow strict-local */ +'use strict'; + +const RNTesterStatePersister = require('../utils/RNTesterStatePersister'); const React = require('react'); const {StyleSheet, Switch, Text, View} = require('react-native'); +class RNTesterSettingSwitchRow extends React.Component< + $FlowFixMeProps, + $FlowFixMeState, +> { + UNSAFE_componentWillReceiveProps(newProps: $FlowFixMeProps) { + const {onEnable, onDisable, persister} = this.props; + if (newProps.persister.state !== persister.state) { + newProps.persister.state ? onEnable() : onDisable(); + } + } + render(): React.Node { + const {label, persister} = this.props; + return ( + + {label} + { + persister.setState(() => value); + }} + /> + + ); + } +} const styles = StyleSheet.create({ row: { padding: 10, @@ -19,22 +47,15 @@ const styles = StyleSheet.create({ justifyContent: 'space-between', }, }); - -export const RNTesterSettingSwitchRow = ({ - label, - onEnable, - onDisable, - active, -}: $FlowFixMeProps): React.Node => { - return ( - - {label} - { - active ? onDisable() : onEnable(); - }} - /> - - ); -}; +/* $FlowFixMe[cannot-reassign-export] (>=0.85.0 site=react_native_fb) This + * comment suppresses an error found when Flow v0.85 was deployed. To see the + * error, delete this comment and run Flow. */ +// $FlowFixMe[cannot-reassign] +RNTesterSettingSwitchRow = RNTesterStatePersister.createContainer( + RNTesterSettingSwitchRow, + { + cacheKeySuffix: ({label}) => 'Switch:' + label, + getInitialState: ({initialValue}) => initialValue, + }, +); +module.exports = RNTesterSettingSwitchRow; diff --git a/packages/rn-tester/js/examples/NativeAnimation/NativeAnimationsExample.js b/packages/rn-tester/js/examples/NativeAnimation/NativeAnimationsExample.js index d2d8ba2ffec..74a0da22cb8 100644 --- a/packages/rn-tester/js/examples/NativeAnimation/NativeAnimationsExample.js +++ b/packages/rn-tester/js/examples/NativeAnimation/NativeAnimationsExample.js @@ -158,45 +158,74 @@ class LoopExample extends React.Component<{...}, $FlowFixMeState> { } } -const RNTesterSettingSwitchRow = - require('../../components/RNTesterSettingSwitchRow').RNTesterSettingSwitchRow; -const RNTesterJsStallsContext = require('../../utils/RNTesterJsStallsContext'); - -const InternalSettings = () => { - const { - state, - onDisableForceJsStalls, - onEnableForceJsStalls, - onEnableJsStallsTracking, - onDisableJsStallsTracking, - } = React.useContext(RNTesterJsStallsContext.RNTesterJsStallsContext); - - const {stallInterval, busyTime, filteredStall, tracking} = state; - return ( - - - - {tracking && ( - - {`JS Stall filtered: ${Math.round(filteredStall)}, `} - {`last: ${busyTime !== null ? busyTime.toFixed(8) : ''}`} - - )} - - ); -}; +const RNTesterSettingSwitchRow = require('../../components/RNTesterSettingSwitchRow'); +class InternalSettings extends React.Component< + {...}, + { + busyTime: number | string, + filteredStall: number, + ... + }, +> { + _stallInterval: ?number; + render() { + return ( + + { + /* $FlowFixMe[incompatible-type] (>=0.63.0 site=react_native_fb) + * This comment suppresses an error found when Flow v0.63 was + * deployed. To see the error delete this comment and run Flow. */ + this._stallInterval = setInterval(() => { + const start = Date.now(); + console.warn('burn CPU'); + while (Date.now() - start < 100) {} + }, 300); + }} + onDisable={() => { + /* $FlowFixMe[incompatible-call] (>=0.63.0 site=react_native_fb) + * This comment suppresses an error found when Flow v0.63 was + * deployed. To see the error delete this comment and run Flow. */ + clearInterval(this._stallInterval || 0); + }} + /> + { + require('react-native/Libraries/Interaction/JSEventLoopWatchdog').install( + { + thresholdMS: 25, + }, + ); + this.setState({busyTime: ''}); + require('react-native/Libraries/Interaction/JSEventLoopWatchdog').addHandler( + { + onStall: ({busyTime}) => + this.setState(state => ({ + busyTime, + filteredStall: + (state.filteredStall || 0) * 0.97 + busyTime * 0.03, + })), + }, + ); + }} + onDisable={() => { + console.warn('Cannot disable yet....'); + }} + /> + {this.state && ( + + {`JS Stall filtered: ${Math.round(this.state.filteredStall)}, `} + {`last: ${this.state.busyTime}`} + + )} + + ); + } +} class EventExample extends React.Component<{...}, $FlowFixMeState> { state = { diff --git a/packages/rn-tester/js/types/RNTesterTypes.js b/packages/rn-tester/js/types/RNTesterTypes.js index 6d000c7847f..e6f3d9142a3 100644 --- a/packages/rn-tester/js/types/RNTesterTypes.js +++ b/packages/rn-tester/js/types/RNTesterTypes.js @@ -59,7 +59,7 @@ export type ScreenTypes = 'components' | 'apis' | 'bookmarks' | null; export type ComponentList = null | {components: string[], apis: string[]}; -export type RNTesterNavState = { +export type RNTesterState = { activeModuleKey: null | string, activeModuleTitle: null | string, activeModuleExampleKey: null | string, @@ -67,10 +67,3 @@ export type RNTesterNavState = { bookmarks: ComponentList, recentlyUsed: ComponentList, }; - -export type RNTesterJsStallsState = { - stallInterval: null | number, - busyTime: null | number, - filteredStall: number, - tracking: boolean, -}; diff --git a/packages/rn-tester/js/utils/RNTesterJsStallsContext.js b/packages/rn-tester/js/utils/RNTesterJsStallsContext.js deleted file mode 100644 index dd3b6fc5d2b..00000000000 --- a/packages/rn-tester/js/utils/RNTesterJsStallsContext.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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. - * - * @format - * @flow - */ - -import React from 'react'; -import type {Context, Element} from 'react'; -import useJsStallsReducer from './useJsStallsReducer'; - -const RNTesterJsStallsContext: Context = React.createContext(); - -const RNTesterJsStallsProvider = (props: $FlowFixMeProps): Element => { - const { - state, - onEnableForceJsStalls, - onDisableForceJsStalls, - onEnableJsStallsTracking, - onDisableJsStallsTracking, - } = useJsStallsReducer(); - const context = { - state, - onEnableForceJsStalls, - onDisableForceJsStalls, - onEnableJsStallsTracking, - onDisableJsStallsTracking, - }; - return ; -}; - -export {RNTesterJsStallsContext, RNTesterJsStallsProvider}; diff --git a/packages/rn-tester/js/utils/RNTesterJsStallsReducer.js b/packages/rn-tester/js/utils/RNTesterJsStallsReducer.js deleted file mode 100644 index 61fea13b6df..00000000000 --- a/packages/rn-tester/js/utils/RNTesterJsStallsReducer.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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. - * - * @format - * @flow - */ - -import type {RNTesterJsStallsState} from '../types/RNTesterTypes'; - -export const RNTesterJsStallsActionsType = { - START_JS_STALLS: 'START_JS_STALLS', - STOP_JS_STALLS: 'STOP_JS_STALLS', - START_TRACK_JS_STALLS: 'START_TRACK_JS_STALLS', - UPDATE_TRACK_JS_STALLS: 'UPDATE_TRACK_JS_STALLS', - STOP_TRACK_JS_STALLS: 'STOP_TRACK_JS_STALLS', -}; - -export const RNTesterJsStallsReducer = ( - state: RNTesterJsStallsState, - action: {type: string, data: RNTesterJsStallsState}, -): RNTesterJsStallsState => { - const {type, data} = action; - switch (type) { - case RNTesterJsStallsActionsType.START_JS_STALLS: - case RNTesterJsStallsActionsType.STOP_JS_STALLS: - return { - ...state, - stallInterval: data.stallInterval, - }; - case RNTesterJsStallsActionsType.START_TRACK_JS_STALLS: - return { - ...state, - tracking: true, - }; - case RNTesterJsStallsActionsType.UPDATE_TRACK_JS_STALLS: - if (!state.stallInterval) { - return { - ...state, - }; - } - return { - ...state, - busyTime: data.busyTime || state.busyTime, - filteredStall: state.filteredStall * 0.97 + (data.busyTime || 0) * 0.03, - }; - case RNTesterJsStallsActionsType.STOP_TRACK_JS_STALLS: - return { - ...state, - }; - default: - throw new Error(`Invalid action type ${action.type}`); - } -}; diff --git a/packages/rn-tester/js/utils/RNTesterNavReducer.js b/packages/rn-tester/js/utils/RNTesterReducer.js similarity index 84% rename from packages/rn-tester/js/utils/RNTesterNavReducer.js rename to packages/rn-tester/js/utils/RNTesterReducer.js index 8be3e9fbc45..d0b0444905f 100644 --- a/packages/rn-tester/js/utils/RNTesterNavReducer.js +++ b/packages/rn-tester/js/utils/RNTesterReducer.js @@ -8,9 +8,9 @@ * @flow */ -import type {RNTesterNavState, ComponentList} from '../types/RNTesterTypes'; +import type {RNTesterState, ComponentList} from '../types/RNTesterTypes'; -export const RNTesterNavActionsType = { +export const RNTesterActionsType = { INIT_FROM_STORAGE: 'INIT_FROM_STORAGE', NAVBAR_PRESS: 'NAVBAR_PRESS', BOOKMARK_PRESS: 'BOOKMARK_PRESS', @@ -67,14 +67,14 @@ const getUpdatedRecentlyUsed = ({ return updatedRecentlyUsed; }; -export const RNTesterNavReducer = ( - state: RNTesterNavState, +export const RNTesterReducer = ( + state: RNTesterState, action: {type: string, data: any}, -): RNTesterNavState => { +): RNTesterState => { switch (action.type) { - case RNTesterNavActionsType.INIT_FROM_STORAGE: + case RNTesterActionsType.INIT_FROM_STORAGE: return action.data; - case RNTesterNavActionsType.NAVBAR_PRESS: + case RNTesterActionsType.NAVBAR_PRESS: return { ...state, activeModuleKey: null, @@ -82,7 +82,7 @@ export const RNTesterNavReducer = ( activeModuleExampleKey: null, screen: action.data.screen, }; - case RNTesterNavActionsType.MODULE_CARD_PRESS: + case RNTesterActionsType.MODULE_CARD_PRESS: return { ...state, activeModuleKey: action.data.key, @@ -94,12 +94,12 @@ export const RNTesterNavReducer = ( recentlyUsed: state.recentlyUsed, }), }; - case RNTesterNavActionsType.EXAMPLE_CARD_PRESS: + case RNTesterActionsType.EXAMPLE_CARD_PRESS: return { ...state, activeModuleExampleKey: action.data.key, }; - case RNTesterNavActionsType.BOOKMARK_PRESS: + case RNTesterActionsType.BOOKMARK_PRESS: return { ...state, bookmarks: getUpdatedBookmarks({ @@ -108,7 +108,7 @@ export const RNTesterNavReducer = ( bookmarks: state.bookmarks, }), }; - case RNTesterNavActionsType.BACK_BUTTON_PRESS: + case RNTesterActionsType.BACK_BUTTON_PRESS: // Go back to module or list return { ...state, diff --git a/packages/rn-tester/js/utils/RNTesterStatePersister.js b/packages/rn-tester/js/utils/RNTesterStatePersister.js new file mode 100644 index 00000000000..495de54a5c0 --- /dev/null +++ b/packages/rn-tester/js/utils/RNTesterStatePersister.js @@ -0,0 +1,82 @@ +/** + * 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. + * + * @format + * @flow + */ + +const React = require('react'); +import {AsyncStorage} from 'react-native'; + +export type PassProps = { + state: State, + setState: (stateLamda: (state: State) => State) => void, + ... +}; + +/** + * A simple container for persisting some state and passing it into the wrapped component as + * `props.persister.state`. Update it with `props.persister.setState`. The component is initially + * rendered using `getInitialState` in the spec and is then re-rendered with the persisted data + * once it's fetched. + * + * This is currently tied to RNTester because it's generally not good to use AsyncStorage like + * this in real apps with user data, but we could maybe pull it out for other internal settings-type + * usage. + */ +function createContainer( + Component: React.ComponentType, ...}>, + spec: { + cacheKeySuffix: (props: Props) => string, + getInitialState: (props: Props) => State, + version?: string, + ... + }, +): React.ComponentType { + return class ComponentWithPersistedState extends React.Component< + Props, + $FlowFixMeState, + > { + static displayName = `RNTesterStatePersister(${String( + Component.displayName ?? Component.name, + )})`; + state = {value: spec.getInitialState(this.props)}; + _cacheKey = `RNTester:${spec.version || 'v1'}:${spec.cacheKeySuffix( + this.props, + )}`; + componentDidMount() { + AsyncStorage.getItem(this._cacheKey, (err, value) => { + if (!err && value) { + this.setState({value: JSON.parse(value)}); + } + }); + } + _passSetState = (stateLamda: (state: State) => State): void => { + this.setState(state => { + const value = stateLamda(state.value); + AsyncStorage.setItem(this._cacheKey, JSON.stringify(value)); + return {value}; + }); + }; + render(): React.Node { + return ( + + ); + } + }; +} + +const RNTesterStatePersister = { + createContainer, +}; + +module.exports = RNTesterStatePersister; diff --git a/packages/rn-tester/js/utils/testerStateUtils.js b/packages/rn-tester/js/utils/testerStateUtils.js index 172260e9b91..bdfa0a590b7 100644 --- a/packages/rn-tester/js/utils/testerStateUtils.js +++ b/packages/rn-tester/js/utils/testerStateUtils.js @@ -8,12 +8,13 @@ * @flow */ +import {AsyncStorage} from 'react-native'; + import RNTesterList from './RNTesterList'; import type { ExamplesList, - RNTesterNavState, - RNTesterJsStallsState, + RNTesterState, ComponentList, } from '../types/RNTesterTypes'; @@ -23,20 +24,13 @@ export const Screens = { BOOKMARKS: 'bookmarks', }; -export const initialNavState: RNTesterNavState = { +export const initialState: RNTesterState = { activeModuleKey: null, activeModuleTitle: null, activeModuleExampleKey: null, - screen: Screens.COMPONENTS, - bookmarks: {components: [], apis: []}, - recentlyUsed: {components: [], apis: []}, -}; - -export const initialJsStallsState: RNTesterJsStallsState = { - stallInterval: null, - busyTime: null, - filteredStall: 0, - tracking: false, + screen: null, + bookmarks: null, + recentlyUsed: null, }; const filterEmptySections = (examplesList: ExamplesList): any => { @@ -133,3 +127,22 @@ export const getExamplesListWithBookmarksAndRecentlyUsed = ({ return filterEmptySections(examplesList); }; + +export const getInitialStateFromAsyncStorage = async ( + storageKey: string, +): Promise => { + const initialStateString = await AsyncStorage.getItem(storageKey); + + if (!initialStateString) { + return { + activeModuleKey: null, + activeModuleTitle: null, + activeModuleExampleKey: null, + screen: Screens.COMPONENTS, + bookmarks: {components: [], apis: []}, + recentlyUsed: {components: [], apis: []}, + }; + } else { + return JSON.parse(initialStateString); + } +}; diff --git a/packages/rn-tester/js/utils/useAsyncStorageReducer.js b/packages/rn-tester/js/utils/useAsyncStorageReducer.js new file mode 100644 index 00000000000..d9f1842ed4b --- /dev/null +++ b/packages/rn-tester/js/utils/useAsyncStorageReducer.js @@ -0,0 +1,33 @@ +/** + * 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. + * + * @format + * @flow + */ + +import * as React from 'react'; +import {AsyncStorage} from 'react-native'; + +import type {RNTesterState} from '../types/RNTesterTypes'; + +export const useAsyncStorageReducer = ( + reducer: Function, + initialState: RNTesterState, + storageKey: string, +): [RNTesterState, Function] => { + const [state, dispatch] = React.useReducer( + reducer, + initialState, + ); + + React.useEffect(() => { + if (state !== initialState) { + AsyncStorage.setItem(storageKey, JSON.stringify(state)); + } + }, [state, storageKey, initialState]); + + return [state, dispatch]; +}; diff --git a/packages/rn-tester/js/utils/useJsStallsReducer.js b/packages/rn-tester/js/utils/useJsStallsReducer.js deleted file mode 100644 index 9c9525b704b..00000000000 --- a/packages/rn-tester/js/utils/useJsStallsReducer.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * 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. - * - * @format - * @flow - */ - -import * as React from 'react'; - -import type {RNTesterJsStallsState} from '../types/RNTesterTypes'; -import { - RNTesterJsStallsReducer, - RNTesterJsStallsActionsType, -} from './RNTesterJsStallsReducer'; -import {initialJsStallsState} from './testerStateUtils'; - -const useJsStallsReducer = (): { - state: RNTesterJsStallsState, - onEnableForceJsStalls: Function, - onDisableForceJsStalls: Function, - onEnableJsStallsTracking: Function, - onDisableJsStallsTracking: Function, -} => { - const [state, dispatch] = React.useReducer( - RNTesterJsStallsReducer, - initialJsStallsState, - ); - - const {stallInterval} = state; - - const onDisableForceJsStalls = () => { - clearInterval(stallInterval || 0); - dispatch({ - type: RNTesterJsStallsActionsType.STOP_JS_STALLS, - data: { - stallInterval: null, - }, - }); - }; - - const onEnableForceJsStalls = () => { - const intervalId = setInterval(() => { - const start = Date.now(); - console.warn('burn CPU'); - while (Date.now() - start < 100) {} - }, 300); - dispatch({ - type: RNTesterJsStallsActionsType.START_JS_STALLS, - data: {stallInterval: intervalId}, - }); - }; - - const onEnableJsStallsTracking = () => { - require('react-native/Libraries/Interaction/JSEventLoopWatchdog').install({ - thresholdMS: 25, - }); - dispatch({ - type: RNTesterJsStallsActionsType.START_TRACK_JS_STALLS, - }); - require('react-native/Libraries/Interaction/JSEventLoopWatchdog').addHandler( - { - onStall: ({busyTime}) => { - dispatch({ - type: RNTesterJsStallsActionsType.UPDATE_TRACK_JS_STALLS, - data: { - busyTime, - }, - }); - }, - }, - ); - }; - - const onDisableJsStallsTracking = () => { - console.warn('Cannot disable yet....'); - }; - - return { - state, - onDisableForceJsStalls, - onEnableForceJsStalls, - onEnableJsStallsTracking, - onDisableJsStallsTracking, - }; -}; - -export default useJsStallsReducer;