Reputation: 4023
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])
Upvotes: 2
Views: 12251
Reputation: 6052
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
Upvotes: 9