Remove AsyncStorage from rn-tester and fix InternalSettings Example
Summary: Changelog: [**General**][**Removed**] - Removed AsyncStorage usage from RNTester As part of the "Lean Core" efforts (see https://github.com/react-native-community/discussions-and-proposals/issues/6) to remove outdated and/or unused components (status: https://gist.github.com/Simek/88a9f1a014a47c37f4fce3738864d2e1), this diff removes usage of the deprecated AsyncStorage API from RNTester. RNTester is intended as a reference to showcase various components and APIs. The implications of the replacement of AsyncStorage in RNTester with in-memory management of state is a tradeoff of persistance to a lighter weight implementation and user predictable behavior. 1. Removed AsyncStorage from rn-tester - removed Navigation and bookmark persisting from reducer - moved JS Stalls and tracking to application state with context and reducer 2. Fixed InternalSettings Example bugs Reviewed By: lunaleaps Differential Revision: D35435562 fbshipit-source-id: a879787d8683a1c452e5b6b75a9e01f3ceadfe5d
This commit is contained in:
Родитель
361b9a808c
Коммит
f4c4f446e4
|
@ -23,29 +23,29 @@ import RNTesterNavBar, {navBarHeight} from './components/RNTesterNavbar';
|
|||
import RNTesterList from './utils/RNTesterList';
|
||||
import {
|
||||
Screens,
|
||||
initialState,
|
||||
initialNavState,
|
||||
getExamplesListWithBookmarksAndRecentlyUsed,
|
||||
getInitialStateFromAsyncStorage,
|
||||
} from './utils/testerStateUtils';
|
||||
import {useAsyncStorageReducer} from './utils/useAsyncStorageReducer';
|
||||
import {RNTesterReducer, RNTesterActionsType} from './utils/RNTesterReducer';
|
||||
import {
|
||||
RNTesterNavReducer,
|
||||
RNTesterNavActionsType,
|
||||
} from './utils/RNTesterNavReducer';
|
||||
import {RNTesterThemeContext, themes} from './components/RNTesterTheme';
|
||||
import RNTTitleBar from './components/RNTTitleBar';
|
||||
import {RNTesterEmptyBookmarksState} from './components/RNTesterEmptyBookmarksState';
|
||||
import {RNTesterJsStallsProvider} from './utils/RNTesterJsStallsContext';
|
||||
|
||||
const APP_STATE_KEY = 'RNTesterAppState.v3';
|
||||
|
||||
// RNTester App currently uses AsyncStorage from react-native for storing navigation state
|
||||
// RNTester App currently uses in memory storage for storing navigation state
|
||||
// and bookmark items.
|
||||
// TODO: Vendor AsyncStorage or create our own.
|
||||
// TODO: Identify/implement solution for async device storage.
|
||||
LogBox.ignoreLogs([/AsyncStorage has been extracted from react-native/]);
|
||||
|
||||
const RNTesterApp = (): React.Node => {
|
||||
const [state, dispatch] = useAsyncStorageReducer(
|
||||
RNTesterReducer,
|
||||
initialState,
|
||||
APP_STATE_KEY,
|
||||
const [state, dispatch] = React.useReducer<Function, Object>(
|
||||
RNTesterNavReducer,
|
||||
initialNavState,
|
||||
);
|
||||
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
const {
|
||||
|
@ -57,17 +57,6 @@ 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}),
|
||||
|
@ -76,7 +65,7 @@ const RNTesterApp = (): React.Node => {
|
|||
|
||||
const handleBackPress = React.useCallback(() => {
|
||||
if (activeModuleKey != null) {
|
||||
dispatch({type: RNTesterActionsType.BACK_BUTTON_PRESS});
|
||||
dispatch({type: RNTesterNavActionsType.BACK_BUTTON_PRESS});
|
||||
}
|
||||
}, [dispatch, activeModuleKey]);
|
||||
|
||||
|
@ -103,7 +92,7 @@ const RNTesterApp = (): React.Node => {
|
|||
const handleModuleCardPress = React.useCallback(
|
||||
({exampleType, key, title}) => {
|
||||
dispatch({
|
||||
type: RNTesterActionsType.MODULE_CARD_PRESS,
|
||||
type: RNTesterNavActionsType.MODULE_CARD_PRESS,
|
||||
data: {exampleType, key, title},
|
||||
});
|
||||
},
|
||||
|
@ -113,7 +102,7 @@ const RNTesterApp = (): React.Node => {
|
|||
const handleModuleExampleCardPress = React.useCallback(
|
||||
exampleName => {
|
||||
dispatch({
|
||||
type: RNTesterActionsType.EXAMPLE_CARD_PRESS,
|
||||
type: RNTesterNavActionsType.EXAMPLE_CARD_PRESS,
|
||||
data: {key: exampleName},
|
||||
});
|
||||
},
|
||||
|
@ -123,7 +112,7 @@ const RNTesterApp = (): React.Node => {
|
|||
const toggleBookmark = React.useCallback(
|
||||
({exampleType, key}) => {
|
||||
dispatch({
|
||||
type: RNTesterActionsType.BOOKMARK_PRESS,
|
||||
type: RNTesterNavActionsType.BOOKMARK_PRESS,
|
||||
data: {exampleType, key},
|
||||
});
|
||||
},
|
||||
|
@ -133,7 +122,7 @@ const RNTesterApp = (): React.Node => {
|
|||
const handleNavBarPress = React.useCallback(
|
||||
args => {
|
||||
dispatch({
|
||||
type: RNTesterActionsType.NAVBAR_PRESS,
|
||||
type: RNTesterNavActionsType.NAVBAR_PRESS,
|
||||
data: {screen: args.screen},
|
||||
});
|
||||
},
|
||||
|
@ -170,40 +159,42 @@ const RNTesterApp = (): React.Node => {
|
|||
|
||||
return (
|
||||
<RNTesterThemeContext.Provider value={theme}>
|
||||
<RNTTitleBar
|
||||
title={title}
|
||||
theme={theme}
|
||||
onBack={activeModule ? handleBackPress : null}
|
||||
documentationURL={activeModule?.documentationURL}
|
||||
/>
|
||||
<View
|
||||
style={StyleSheet.compose(styles.container, {
|
||||
backgroundColor: theme.GroupedBackgroundColor,
|
||||
})}>
|
||||
{activeModule != null ? (
|
||||
<RNTesterModuleContainer
|
||||
module={activeModule}
|
||||
example={activeModuleExample}
|
||||
onExampleCardPress={handleModuleExampleCardPress}
|
||||
/>
|
||||
) : screen === Screens.BOOKMARKS &&
|
||||
examplesList.bookmarks.length === 0 ? (
|
||||
<RNTesterEmptyBookmarksState />
|
||||
) : (
|
||||
<RNTesterModuleList
|
||||
sections={activeExampleList}
|
||||
toggleBookmark={toggleBookmark}
|
||||
handleModuleCardPress={handleModuleCardPress}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.bottomNavbar}>
|
||||
<RNTesterNavBar
|
||||
screen={screen || Screens.COMPONENTS}
|
||||
isExamplePageOpen={!!activeModule}
|
||||
handleNavBarPress={handleNavBarPress}
|
||||
<RNTesterJsStallsProvider>
|
||||
<RNTTitleBar
|
||||
title={title}
|
||||
theme={theme}
|
||||
onBack={activeModule ? handleBackPress : null}
|
||||
documentationURL={activeModule?.documentationURL}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={StyleSheet.compose(styles.container, {
|
||||
backgroundColor: theme.GroupedBackgroundColor,
|
||||
})}>
|
||||
{activeModule != null ? (
|
||||
<RNTesterModuleContainer
|
||||
module={activeModule}
|
||||
example={activeModuleExample}
|
||||
onExampleCardPress={handleModuleExampleCardPress}
|
||||
/>
|
||||
) : screen === Screens.BOOKMARKS &&
|
||||
examplesList.bookmarks.length === 0 ? (
|
||||
<RNTesterEmptyBookmarksState />
|
||||
) : (
|
||||
<RNTesterModuleList
|
||||
sections={activeExampleList}
|
||||
toggleBookmark={toggleBookmark}
|
||||
handleModuleCardPress={handleModuleCardPress}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.bottomNavbar}>
|
||||
<RNTesterNavBar
|
||||
screen={screen || Screens.COMPONENTS}
|
||||
isExamplePageOpen={!!activeModule}
|
||||
handleNavBarPress={handleNavBarPress}
|
||||
/>
|
||||
</View>
|
||||
</RNTesterJsStallsProvider>
|
||||
</RNTesterThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,38 +8,10 @@
|
|||
* @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 (
|
||||
<View style={styles.row}>
|
||||
<Text>{label}</Text>
|
||||
<Switch
|
||||
value={persister.state}
|
||||
onValueChange={value => {
|
||||
persister.setState(() => value);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
padding: 10,
|
||||
|
@ -47,15 +19,22 @@ const styles = StyleSheet.create({
|
|||
justifyContent: 'space-between',
|
||||
},
|
||||
});
|
||||
/* $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;
|
||||
|
||||
export const RNTesterSettingSwitchRow = ({
|
||||
label,
|
||||
onEnable,
|
||||
onDisable,
|
||||
active,
|
||||
}: $FlowFixMeProps): React.Node => {
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
<Text>{label}</Text>
|
||||
<Switch
|
||||
value={active}
|
||||
onValueChange={() => {
|
||||
active ? onDisable() : onEnable();
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -158,74 +158,45 @@ class LoopExample extends React.Component<{...}, $FlowFixMeState> {
|
|||
}
|
||||
}
|
||||
|
||||
const RNTesterSettingSwitchRow = require('../../components/RNTesterSettingSwitchRow');
|
||||
class InternalSettings extends React.Component<
|
||||
{...},
|
||||
{
|
||||
busyTime: number | string,
|
||||
filteredStall: number,
|
||||
...
|
||||
},
|
||||
> {
|
||||
_stallInterval: ?number;
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<RNTesterSettingSwitchRow
|
||||
initialValue={false}
|
||||
label="Force JS Stalls"
|
||||
onEnable={() => {
|
||||
/* $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);
|
||||
}}
|
||||
/>
|
||||
<RNTesterSettingSwitchRow
|
||||
initialValue={false}
|
||||
label="Track JS Stalls"
|
||||
onEnable={() => {
|
||||
require('react-native/Libraries/Interaction/JSEventLoopWatchdog').install(
|
||||
{
|
||||
thresholdMS: 25,
|
||||
},
|
||||
);
|
||||
this.setState({busyTime: '<none>'});
|
||||
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 && (
|
||||
<Text>
|
||||
{`JS Stall filtered: ${Math.round(this.state.filteredStall)}, `}
|
||||
{`last: ${this.state.busyTime}`}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
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 (
|
||||
<View>
|
||||
<RNTesterSettingSwitchRow
|
||||
initialValue={false}
|
||||
label="Force JS Stalls"
|
||||
active={stallInterval !== null}
|
||||
onEnable={onEnableForceJsStalls}
|
||||
onDisable={onDisableForceJsStalls}
|
||||
/>
|
||||
<RNTesterSettingSwitchRow
|
||||
initialValue={false}
|
||||
label="Track JS Stalls"
|
||||
active={tracking}
|
||||
onEnable={onEnableJsStallsTracking}
|
||||
onDisable={onDisableJsStallsTracking}
|
||||
/>
|
||||
{tracking && (
|
||||
<Text>
|
||||
{`JS Stall filtered: ${Math.round(filteredStall)}, `}
|
||||
{`last: ${busyTime !== null ? busyTime.toFixed(8) : '<none>'}`}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
class EventExample extends React.Component<{...}, $FlowFixMeState> {
|
||||
state = {
|
||||
|
|
|
@ -59,7 +59,7 @@ export type ScreenTypes = 'components' | 'apis' | 'bookmarks' | null;
|
|||
|
||||
export type ComponentList = null | {components: string[], apis: string[]};
|
||||
|
||||
export type RNTesterState = {
|
||||
export type RNTesterNavState = {
|
||||
activeModuleKey: null | string,
|
||||
activeModuleTitle: null | string,
|
||||
activeModuleExampleKey: null | string,
|
||||
|
@ -67,3 +67,10 @@ export type RNTesterState = {
|
|||
bookmarks: ComponentList,
|
||||
recentlyUsed: ComponentList,
|
||||
};
|
||||
|
||||
export type RNTesterJsStallsState = {
|
||||
stallInterval: null | number,
|
||||
busyTime: null | number,
|
||||
filteredStall: number,
|
||||
tracking: boolean,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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<any> = React.createContext();
|
||||
|
||||
const RNTesterJsStallsProvider = (props: $FlowFixMeProps): Element<any> => {
|
||||
const {
|
||||
state,
|
||||
onEnableForceJsStalls,
|
||||
onDisableForceJsStalls,
|
||||
onEnableJsStallsTracking,
|
||||
onDisableJsStallsTracking,
|
||||
} = useJsStallsReducer();
|
||||
const context = {
|
||||
state,
|
||||
onEnableForceJsStalls,
|
||||
onDisableForceJsStalls,
|
||||
onEnableJsStallsTracking,
|
||||
onDisableJsStallsTracking,
|
||||
};
|
||||
return <RNTesterJsStallsContext.Provider {...props} value={context} />;
|
||||
};
|
||||
|
||||
export {RNTesterJsStallsContext, RNTesterJsStallsProvider};
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* 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}`);
|
||||
}
|
||||
};
|
|
@ -8,9 +8,9 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {RNTesterState, ComponentList} from '../types/RNTesterTypes';
|
||||
import type {RNTesterNavState, ComponentList} from '../types/RNTesterTypes';
|
||||
|
||||
export const RNTesterActionsType = {
|
||||
export const RNTesterNavActionsType = {
|
||||
INIT_FROM_STORAGE: 'INIT_FROM_STORAGE',
|
||||
NAVBAR_PRESS: 'NAVBAR_PRESS',
|
||||
BOOKMARK_PRESS: 'BOOKMARK_PRESS',
|
||||
|
@ -67,14 +67,14 @@ const getUpdatedRecentlyUsed = ({
|
|||
return updatedRecentlyUsed;
|
||||
};
|
||||
|
||||
export const RNTesterReducer = (
|
||||
state: RNTesterState,
|
||||
export const RNTesterNavReducer = (
|
||||
state: RNTesterNavState,
|
||||
action: {type: string, data: any},
|
||||
): RNTesterState => {
|
||||
): RNTesterNavState => {
|
||||
switch (action.type) {
|
||||
case RNTesterActionsType.INIT_FROM_STORAGE:
|
||||
case RNTesterNavActionsType.INIT_FROM_STORAGE:
|
||||
return action.data;
|
||||
case RNTesterActionsType.NAVBAR_PRESS:
|
||||
case RNTesterNavActionsType.NAVBAR_PRESS:
|
||||
return {
|
||||
...state,
|
||||
activeModuleKey: null,
|
||||
|
@ -82,7 +82,7 @@ export const RNTesterReducer = (
|
|||
activeModuleExampleKey: null,
|
||||
screen: action.data.screen,
|
||||
};
|
||||
case RNTesterActionsType.MODULE_CARD_PRESS:
|
||||
case RNTesterNavActionsType.MODULE_CARD_PRESS:
|
||||
return {
|
||||
...state,
|
||||
activeModuleKey: action.data.key,
|
||||
|
@ -94,12 +94,12 @@ export const RNTesterReducer = (
|
|||
recentlyUsed: state.recentlyUsed,
|
||||
}),
|
||||
};
|
||||
case RNTesterActionsType.EXAMPLE_CARD_PRESS:
|
||||
case RNTesterNavActionsType.EXAMPLE_CARD_PRESS:
|
||||
return {
|
||||
...state,
|
||||
activeModuleExampleKey: action.data.key,
|
||||
};
|
||||
case RNTesterActionsType.BOOKMARK_PRESS:
|
||||
case RNTesterNavActionsType.BOOKMARK_PRESS:
|
||||
return {
|
||||
...state,
|
||||
bookmarks: getUpdatedBookmarks({
|
||||
|
@ -108,7 +108,7 @@ export const RNTesterReducer = (
|
|||
bookmarks: state.bookmarks,
|
||||
}),
|
||||
};
|
||||
case RNTesterActionsType.BACK_BUTTON_PRESS:
|
||||
case RNTesterNavActionsType.BACK_BUTTON_PRESS:
|
||||
// Go back to module or list
|
||||
return {
|
||||
...state,
|
|
@ -1,82 +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
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
import {AsyncStorage} from 'react-native';
|
||||
|
||||
export type PassProps<State> = {
|
||||
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<Props: Object, State>(
|
||||
Component: React.ComponentType<Props & {persister: PassProps<State>, ...}>,
|
||||
spec: {
|
||||
cacheKeySuffix: (props: Props) => string,
|
||||
getInitialState: (props: Props) => State,
|
||||
version?: string,
|
||||
...
|
||||
},
|
||||
): React.ComponentType<Props> {
|
||||
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 (
|
||||
<Component
|
||||
{...this.props}
|
||||
persister={{
|
||||
state: this.state.value,
|
||||
setState: this._passSetState,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const RNTesterStatePersister = {
|
||||
createContainer,
|
||||
};
|
||||
|
||||
module.exports = RNTesterStatePersister;
|
|
@ -8,13 +8,12 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import {AsyncStorage} from 'react-native';
|
||||
|
||||
import RNTesterList from './RNTesterList';
|
||||
|
||||
import type {
|
||||
ExamplesList,
|
||||
RNTesterState,
|
||||
RNTesterNavState,
|
||||
RNTesterJsStallsState,
|
||||
ComponentList,
|
||||
} from '../types/RNTesterTypes';
|
||||
|
||||
|
@ -24,13 +23,20 @@ export const Screens = {
|
|||
BOOKMARKS: 'bookmarks',
|
||||
};
|
||||
|
||||
export const initialState: RNTesterState = {
|
||||
export const initialNavState: RNTesterNavState = {
|
||||
activeModuleKey: null,
|
||||
activeModuleTitle: null,
|
||||
activeModuleExampleKey: null,
|
||||
screen: null,
|
||||
bookmarks: null,
|
||||
recentlyUsed: null,
|
||||
screen: Screens.COMPONENTS,
|
||||
bookmarks: {components: [], apis: []},
|
||||
recentlyUsed: {components: [], apis: []},
|
||||
};
|
||||
|
||||
export const initialJsStallsState: RNTesterJsStallsState = {
|
||||
stallInterval: null,
|
||||
busyTime: null,
|
||||
filteredStall: 0,
|
||||
tracking: false,
|
||||
};
|
||||
|
||||
const filterEmptySections = (examplesList: ExamplesList): any => {
|
||||
|
@ -127,22 +133,3 @@ export const getExamplesListWithBookmarksAndRecentlyUsed = ({
|
|||
|
||||
return filterEmptySections(examplesList);
|
||||
};
|
||||
|
||||
export const getInitialStateFromAsyncStorage = async (
|
||||
storageKey: string,
|
||||
): Promise<RNTesterState> => {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,33 +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 {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<Function, Object>(
|
||||
reducer,
|
||||
initialState,
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (state !== initialState) {
|
||||
AsyncStorage.setItem(storageKey, JSON.stringify(state));
|
||||
}
|
||||
}, [state, storageKey, initialState]);
|
||||
|
||||
return [state, dispatch];
|
||||
};
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* 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<Function, Object>(
|
||||
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;
|
Загрузка…
Ссылка в новой задаче