I'm trying to convert the class component of React Native to a functional component that involves useRef. Following is the class component:
import React, { Component } from "react";
import { AppRegistry, StyleSheet, Text, View, Animated, TouchableWithoutFeedback } from "react-native";
import { interpolateNumber, interpolateRgb } from "d3-interpolate";
export default class animations extends Component {
state = {
animation: new Animated.Value(0)
};
componentWillMount() {
const positionInterpolate = interpolateNumber(0, 200);
const colorInterpolate = interpolateRgb("rgb(255,99,71)", "rgb(99,71,255)");;
this.state.animation.addListener(({value}) => {
const position = positionInterpolate(value);
const color = colorInterpolate(value);
const style = [
styles.box,
{
backgroundColor: color,
transform: [
{translateY: position}
]
}
];
this._view.setNativeProps({ style });
});
}
handlePress = () => {
Animated.timing(this.state.animation, {
toValue: 1,
duration: 500,
}).start();
}
render() {
return (
<View style={styles.container}>
<TouchableWithoutFeedback onPress={this.handlePress}>
<View style={styles.box} ref={view => this._view = view} />
</TouchableWithoutFeedback>
</View>
);
}
}
When I convert to the following functional component, I get the following error:
TypeError that someRef.setNativeProps is not a function. (In 'someRef.setNativeProps({ style: style })', 'someRef.setNativeProps' is undefined)
import
React,
{
useState,
useEffect,
useRef
} from 'react'
import {
View,
Animated,
TouchableWithoutFeedback,
StyleSheet
} from 'react-native'
import {
interpolateNumber,
interpolateRgb,
} from 'd3-interpolate'
export default d3number = () => {
const [animation] = useState(new Animated.Value(1))
const [position, setPosition] = useState()
const someRef = useRef(null)
const startAnimation = () => {
Animated.timing(animation, {
toValue: 2,
duration: 1500,
}).start()
}
const positionInterpolate = interpolateNumber(0, 300)
useEffect(() => {
animation.addListener(({value}) => {
const pos = positionInterpolate(value)
setPosition(pos)
})
const style = [
styles.box,
{
transform: [
{
translateY: position
}
]
}
]
someRef.setNativeProps({ style })
// console.log(someRef.setNativeProps)
}, [])
return (
<View style={styles.container}>
<TouchableWithoutFeedback onPress={startAnimation}>
<View
style={styles.box}
ref={someRef}
/>
</TouchableWithoutFeedback>
</View>
)
}
Update: this was resolved by including the style within the listener
if (!someRef.current) {
return;
}
animation.addListener(({value}) => {
const style = [
styles.box,
{
transform: [
{
translateY: positionInterpolate(value)
}
]
}
]
someRef.current.setNativeProps({ style })
})
// console.log(someRef.setNativeProps)
}, [someRef.current])
As per the docs [1] The value of the ref is kept at ref.current. So you need to run someRef.current.setNativeProps.
That said your useEffect code will have problems because someRef will be set to null on your first render when useEffect is running. You'll have to add someRef.current to the useEffect dependencies array so the effect is called whenever someRef updates and add a condition to not call someRef.current.setNativeProps when someRef.current is null. You may want to skip the entire useEffect body unless someRef.current is not null.
useEffect(() => {
if (!someRef.current) {
return;
}
animation.addListener(({value}) => {
const pos = positionInterpolate(value)
setPosition(pos)
})
const style = [
styles.box,
{
transform: [
{
translateY: position
}
]
}
]
someRef.current.setNativeProps({ style })
}, [someRef.current]) # note the addition of `someRef.current` here so the effect runs again when the ref is set
[1] https://reactjs.org/docs/hooks-reference.html#useref
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With