Fix RNTester strict mode warnings (#48620)

Summary:
Follow up from https://github.com/facebook/react-native/issues/48619. While investigating https://github.com/facebook/react-native/issues/22186, some false positives showed up as some of the examples also have non-strict mode compatible code.

In this PR: Converting from class to functional components some `AnExApp` examples that were using `UNSAFE_` lifecycles.

## Changelog:

[INTERNAL] - Fix RNTester strict mode warnings for AnExApp examples

Pull Request resolved: https://github.com/facebook/react-native/pull/48620

Test Plan:
- Wrapped the the entry app component in `RNTesterAppShared.js` with `StrictMode` and verified that no warnings are shown anymore for the updated components.
- Checked the examples are still working as they were.

Reviewed By: rshest

Differential Revision: D68092958

Pulled By: cipolleschi

fbshipit-source-id: 0f2cea3c679f8db0f13054e2851a73dc23a4c906
This commit is contained in:
Mateo Guzmán
2025-01-13 04:46:57 -08:00
committed by Facebook GitHub Bot
parent 2f2281718a
commit cc4e4c7fec
6 changed files with 213 additions and 219 deletions
@@ -10,15 +10,15 @@
'use strict';
const AnExSet = require('./AnExSet');
const React = require('react');
const {
import AnExSet from './AnExSet';
import React from 'react';
import {
Animated,
LayoutAnimation,
PanResponder,
StyleSheet,
View,
} = require('react-native');
} from 'react-native';
const CIRCLE_SIZE = 80;
const CIRCLE_MARGIN = 18;
@@ -188,7 +188,7 @@ class Circle extends React.Component<any, any> {
</Animated.View>
);
}
_toggleIsActive = (velocity: void) => {
_toggleIsActive = (velocity: ?number) => {
const config = {tension: 30, friction: 7};
if (this.state.isActive) {
Animated.spring(this.props.openVal, {
@@ -10,8 +10,8 @@
'use strict';
const React = require('react');
const {Animated, PanResponder, StyleSheet, View} = require('react-native');
import React from 'react';
import {Animated, PanResponder, StyleSheet, View} from 'react-native';
const NUM_BOBBLES = 5;
const RAD_EACH = Math.PI / 2 / (NUM_BOBBLES - 2);
@@ -169,4 +169,4 @@ const BOBBLE_IMGS = [
'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xaf1/t39.1997-6/851562_575284782557566_1188781517_n.png',
];
module.exports = AnExBobble;
export default AnExBobble;
@@ -13,8 +13,8 @@
import type {GestureState} from 'react-native/Libraries/Interaction/PanResponder';
import type {PressEvent} from 'react-native/Libraries/Types/CoreEventTypes';
const React = require('react');
const {Animated, PanResponder, StyleSheet, View} = require('react-native');
import React from 'react';
import {Animated, PanResponder, StyleSheet, View} from 'react-native';
class AnExChained extends React.Component<Object, any> {
constructor(props: Object) {
@@ -119,4 +119,4 @@ const CHAIN_IMGS = [
require('../../assets/bunny.png'),
];
module.exports = AnExChained;
export default AnExChained;
@@ -10,15 +10,15 @@
'use strict';
const React = require('react');
const {
import React from 'react';
import {
Animated,
Image,
ScrollView,
StyleSheet,
Text,
View,
} = require('react-native');
} from 'react-native';
class AnExScroll extends React.Component<$FlowFixMeProps, any> {
state: any = {scrollX: new Animated.Value(0)};
@@ -118,4 +118,4 @@ const BUNNY_PIC = {
uri: 'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xaf1/t39.1997-6/851564_531111380292237_1898871086_n.png',
};
module.exports = AnExScroll;
export default AnExScroll;
@@ -10,100 +10,100 @@
'use strict';
const AnExBobble = require('./AnExBobble');
const AnExChained = require('./AnExChained');
const AnExScroll = require('./AnExScroll');
const AnExTilt = require('./AnExTilt');
const React = require('react');
const {
Animated,
PanResponder,
StyleSheet,
Text,
View,
} = require('react-native');
import AnExBobble from './AnExBobble';
import AnExChained from './AnExChained';
import AnExScroll from './AnExScroll';
import AnExTilt from './AnExTilt';
import React, {useRef, useState} from 'react';
import {Animated, PanResponder, StyleSheet, Text, View} from 'react-native';
class AnExSet extends React.Component<Object, any> {
constructor(props: Object) {
super(props);
function randColor() {
const colors = [0, 1, 2].map(() => Math.floor(Math.random() * 150 + 100));
return 'rgb(' + colors.join(',') + ')';
}
this.state = {
closeColor: randColor(),
openColor: randColor(),
};
}
render(): React.Node {
const backgroundColor = this.props.openVal
? this.props.openVal.interpolate({
inputRange: [0, 1],
outputRange: [
this.state.closeColor, // interpolates color strings
this.state.openColor,
],
})
: this.state.closeColor;
const panelWidth =
(this.props.containerLayout && this.props.containerLayout.width) || 320;
return (
<View style={styles.container}>
<Animated.View
style={[styles.header, {backgroundColor}]}
{...this.state.dismissResponder.panHandlers}>
<Text style={[styles.text, styles.headerText]}>{this.props.id}</Text>
</Animated.View>
{this.props.isActive && (
<View style={styles.stream}>
<View style={styles.card}>
<Text style={styles.text}>July 2nd</Text>
<AnExTilt isActive={this.props.isActive} />
<AnExBobble />
</View>
<AnExScroll panelWidth={panelWidth} />
<AnExChained />
</View>
)}
</View>
);
}
const randColor = () => {
const colors = [0, 1, 2].map(() => Math.floor(Math.random() * 150 + 100));
return `rgb(${colors.join(',')})`;
};
UNSAFE_componentWillMount() {
this.state.dismissY = new Animated.Value(0);
this.state.dismissResponder = PanResponder.create({
onStartShouldSetPanResponder: () => this.props.isActive,
onPanResponderGrant: () => {
Animated.spring(this.props.openVal, {
// Animated value passed in.
toValue: this.state.dismissY.interpolate({
// Track dismiss gesture
inputRange: [0, 300], // and interpolate pixel distance
outputRange: [1, 0], // to a fraction.
}),
type AnExSetProps = $ReadOnly<{|
openVal: Animated.Value,
containerLayout: {width: number, height: number},
id: string,
isActive: boolean,
onDismiss: (velocity: number) => void,
|}>;
const AnExSet = ({
openVal,
containerLayout,
id,
isActive,
onDismiss,
}: AnExSetProps): React.Node => {
const [closeColor] = useState(randColor());
const [openColor] = useState(randColor());
const dismissY = useRef(new Animated.Value(0)).current;
const dismissResponder = PanResponder.create({
onStartShouldSetPanResponder: () => isActive,
onPanResponderGrant: () => {
Animated.spring(openVal, {
// Animated value passed in.
toValue: dismissY.interpolate({
// Track dismiss gesture
inputRange: [0, 300], // and interpolate pixel distance
outputRange: [1, 0], // to a fraction.
}),
useNativeDriver: false,
}).start();
},
onPanResponderMove: Animated.event(
[null, {dy: dismissY}], // track pan gesture
{useNativeDriver: false},
),
onPanResponderRelease: (e, gestureState) => {
if (gestureState.dy > 100) {
onDismiss(gestureState.vy); // delegates dismiss action to parent
} else {
Animated.spring(openVal, {
// animate back open if released early
toValue: 1,
useNativeDriver: false,
}).start();
},
onPanResponderMove: Animated.event(
[null, {dy: this.state.dismissY}], // track pan gesture
{useNativeDriver: false},
),
onPanResponderRelease: (e, gestureState) => {
if (gestureState.dy > 100) {
this.props.onDismiss(gestureState.vy); // delegates dismiss action to parent
} else {
Animated.spring(this.props.openVal, {
// animate back open if released early
toValue: 1,
}
},
});
useNativeDriver: false,
}).start();
}
},
});
}
}
const backgroundColor = openVal
? openVal.interpolate({
inputRange: [0, 1],
outputRange: [
closeColor, // interpolates color strings
openColor,
],
})
: closeColor;
const panelWidth = (containerLayout && containerLayout.width) || 320;
return (
<View style={styles.container}>
<Animated.View
style={[styles.header, {backgroundColor}]}
{...dismissResponder.panHandlers}>
<Text style={[styles.text, styles.headerText]}>{id}</Text>
</Animated.View>
{isActive && (
<View style={styles.stream}>
<View style={styles.card}>
<Text style={styles.text}>July 2nd</Text>
<AnExTilt isActive={isActive} />
<AnExBobble />
</View>
<AnExScroll panelWidth={panelWidth} />
<AnExChained />
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
@@ -144,4 +144,4 @@ const styles = StyleSheet.create({
},
});
module.exports = AnExSet;
export default AnExSet;
@@ -10,77 +10,73 @@
'use strict';
const React = require('react');
const {Animated, PanResponder, StyleSheet} = require('react-native');
import React, {useCallback, useEffect, useRef} from 'react';
import {Animated, PanResponder, StyleSheet} from 'react-native';
class AnExTilt extends React.Component<Object, any> {
constructor(props: Object) {
super(props);
this.state = {
panX: new Animated.Value(0),
opacity: new Animated.Value(1),
burns: new Animated.Value(1.15),
};
// $FlowFixMe[prop-missing]
this.state.tiltPanResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
Animated.timing(this.state.opacity, {
toValue: this.state.panX.interpolate({
inputRange: [-300, 0, 300], // pan is in pixels
outputRange: [0, 1, 0], // goes to zero at both edges
}),
const AnExTilt = (): React.Node => {
const panX = useRef(new Animated.Value(0)).current;
const opacity = useRef(new Animated.Value(1)).current;
const burns = useRef(new Animated.Value(1.15)).current;
// direct tracking
duration: 0,
const tiltPanResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
Animated.timing(opacity, {
toValue: panX.interpolate({
inputRange: [-300, 0, 300], // pan is in pixels
outputRange: [0, 1, 0], // goes to zero at both edges
}),
useNativeDriver: false,
}).start();
},
onPanResponderMove: Animated.event(
[null, {dx: this.state.panX}], // panX is linked to the gesture
{useNativeDriver: false},
),
onPanResponderRelease: (e, gestureState) => {
let toValue = 0;
if (gestureState.dx > 100) {
toValue = 500;
} else if (gestureState.dx < -100) {
toValue = -500;
// direct tracking
duration: 0,
useNativeDriver: false,
}).start();
},
onPanResponderMove: Animated.event(
[null, {dx: panX}], // panX is linked to the gesture
{useNativeDriver: false},
),
onPanResponderRelease: (e, gestureState) => {
let toValue = 0;
if (gestureState.dx > 100) {
toValue = 500;
} else if (gestureState.dx < -100) {
toValue = -500;
}
Animated.spring(panX, {
// animate back to center or off screen
toValue,
// maintain gesture velocity
velocity: gestureState.vx,
tension: 10,
friction: 3,
useNativeDriver: false,
}).start();
panX.removeAllListeners();
const id: any = panX.addListener(({value}) => {
// listen until offscreen
if (Math.abs(value) > 400) {
panX.removeListener(id); // offscreen, so stop listening
Animated.timing(opacity, {
// Fade back in. This unlinks it from tracking panX
toValue: 1,
useNativeDriver: false,
}).start();
panX.setValue(0); // Note: stops the spring animation
toValue !== 0 && startBurnsZoom();
}
Animated.spring(this.state.panX, {
// animate back to center or off screen
toValue,
});
},
});
// maintain gesture velocity
velocity: gestureState.vx,
tension: 10,
friction: 3,
useNativeDriver: false,
}).start();
this.state.panX.removeAllListeners();
const id: any = this.state.panX.addListener(({value}) => {
// listen until offscreen
if (Math.abs(value) > 400) {
this.state.panX.removeListener(id); // offscreen, so stop listening
Animated.timing(this.state.opacity, {
// Fade back in. This unlinks it from tracking this.state.panX
toValue: 1,
useNativeDriver: false,
}).start();
this.state.panX.setValue(0); // Note: stops the spring animation
toValue !== 0 && this._startBurnsZoom();
}
});
},
});
}
_startBurnsZoom() {
this.state.burns.setValue(1); // reset to beginning
Animated.decay(this.state.burns, {
const startBurnsZoom = useCallback(() => {
burns.setValue(1); // reset to beginning
Animated.decay(burns, {
// subtle zoom
velocity: 1,
@@ -89,56 +85,54 @@ class AnExTilt extends React.Component<Object, any> {
useNativeDriver: false,
}).start();
}
}, [burns]);
UNSAFE_componentWillMount() {
this._startBurnsZoom();
}
useEffect(() => {
startBurnsZoom();
}, [startBurnsZoom]);
render(): React.Node {
return (
<Animated.View
{...this.state.tiltPanResponder.panHandlers}
style={[
styles.tilt,
{
opacity: this.state.opacity,
transform: [
{
rotate: this.state.panX.interpolate({
inputRange: [-320, 320],
outputRange: ['-15deg', '15deg'],
}),
}, // interpolate string "shapes"
{translateX: this.state.panX},
],
},
]}>
<Animated.Image
pointerEvents="none"
style={{
flex: 1,
transform: [
{
translateX: this.state.panX.interpolate({
inputRange: [-3, 3], // small range is extended by default
outputRange: [2, -2],
}), // parallax
},
{
scale: this.state.burns.interpolate({
inputRange: [1, 3000],
outputRange: [1, 1.25],
}), // simple multiplier
},
],
}}
source={require('../../assets/trees.jpg')}
/>
</Animated.View>
);
}
}
return (
<Animated.View
{...tiltPanResponder.panHandlers}
style={[
styles.tilt,
{
opacity,
transform: [
{
rotate: panX.interpolate({
inputRange: [-320, 320],
outputRange: ['-15deg', '15deg'],
}),
}, // interpolate string "shapes"
{translateX: panX},
],
},
]}>
<Animated.Image
pointerEvents="none"
style={{
flex: 1,
transform: [
{
translateX: panX.interpolate({
inputRange: [-3, 3], // small range is extended by default
outputRange: [2, -2],
}), // parallax
},
{
scale: burns.interpolate({
inputRange: [1, 3000],
outputRange: [1, 1.25],
}), // simple multiplier
},
],
}}
source={require('../../assets/trees.jpg')}
/>
</Animated.View>
);
};
const styles = StyleSheet.create({
tilt: {
@@ -152,4 +146,4 @@ const styles = StyleSheet.create({
},
});
module.exports = AnExTilt;
export default AnExTilt;