How to use the useRef hook for setNativeProps in React Native?

10,297

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

Share:
10,297

Related videos on Youtube

Kevvv
Author by

Kevvv

Updated on May 26, 2022

Comments

  • Kevvv
    Kevvv almost 2 years

    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])