简体   繁体   中英

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM