cjava
cjava

Reputation: 658

react native ref measureLayout

I am following this tutorial

https://www.youtube.com/watch?v=ZiSN9uik6OY&list=RDCMUCTcH04SRuyedaSuuQVeAcdg&start_radio=1&t=1398

The problem is when I console.log( i.ref.current), I see all value. The ap will just not start with error Undefined is not an Object inside the app.

and in reactive native console following comes up

This error is located at:
    in Tabs (at App.js:118)
    in RCTView (at View.js:34)
    in View (at App.js:91)
    in App (at renderApplication.js:47)
    in RCTView (at View.js:34)
    in View (at AppContainer.js:107)
    in RCTView (at View.js:34)
    in View (at AppContainer.js:134)
    in AppContainer (at renderApplication.js:40)
 ERROR  TypeError: undefined is not an object (evaluating 'nativeNode._nativeTag')


below is the app.js file:

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View, Dimensions, Animated, FlatList, Image } from 'react-native';


const {width, height} = Dimensions.get('screen')

const images = {
  man:
    'https://images.pexels.com/photos/3147528/pexels-photo-3147528.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
  women:
    'https://images.pexels.com/photos/2552130/pexels-photo-2552130.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
  kids:
    'https://images.pexels.com/photos/5080167/pexels-photo-5080167.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
  skullcandy:
    'https://images.pexels.com/photos/5602879/pexels-photo-5602879.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
  help:
    'https://images.pexels.com/photos/6857165/pexels-photo-6857165.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
};
const data = Object.keys(images).map((i) => ({
  key: i,
  title: i,
  image: images[i],
  ref: React.createRef()
}));

const Indicator = () =>{
  return (<View  style={{
    position:'absolute',
    height:4,
    width:100,
    backgroundColor:'white',
    bottom: -10
   }}/>);
 
}

const Tab = React.forwardRef(({item}, ref) => {
  return (
    <View ref={ref}>
      <Text style={{
        color:'rgba(255, 255, 255, 0.8)', 
        fontWeight:'800', 
        fontSize:84/data.length,
        textTransform: 'uppercase' }}>{item.title}</Text>
    </View>
  );
});

const Tabs  = ({data, scrollX}) =>{
  const [measures, setMeasures] = React.useState([])
  const containerRef = React.useRef();

  React.useEffect(() => {
    let m = []
    data.forEach((i) => {
      i.ref.current.measureLayout(
        containerRef.current,
        (x, y, width, height) => {
          m.push({x, y, width, height})
          console.log(x, y, width, height)
        }
      );
        if(m.length === data.length){
          setMeasures(m)
        }
    });
  }, [])
  console.log(measures);
   return( <View style={{ position: 'absolute', top:50, width }}>
      <View style={{justifyContent:'space-evenly', flex:1, flexDirection:'row'}}>
        {
          data.map((item) => {
            return <Tab key={item.key} item={item} ref={item.ref} />
          }) 
        }
      </View>
      <Indicator/>
    </View>
  )

}


export default function App() {
  const scrollX = React.useRef(new Animated.Value(0)).current;


  return (
    <View style={styles.container}>
      <StatusBar hidden />
      <Animated.FlatList 
        data={data}
        keyExtractor={item => item.key}
        renderItem={({item}) => {
            return (<View style={{width, height}}>
              <Image 
                source={{uri: item.image}} 
                style={{flex:1, resizeMode:'cover'}} 
                />
                <View style={
                   [
                     StyleSheet.absoluteFillObject,
                     {backgroundColor: 'rgba(0,0,0, 0.3)' }
                   ]}/>
            </View>)
          }
        }
        horizontal
        showsHorizontalScrollIndicator={false}
        pagingEnabled
        onScroll={Animated.event([{nativeEvent: {contentOffset :{x: scrollX } }}],
          {useNativeDriver: false}
          )}
        bounces={false}
        />
        <Tabs scrollX={scrollX} data={data} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
}); ```

Thanks. appreciate your time reading this

Upvotes: 2

Views: 7827

Answers (1)

bas
bas

Reputation: 15722

The problem is that you haven't attached containerRef to any View.

Since the ref isn't attached to a component containerRef.current evaluates to undefined, causing the logic in the useEffect to break.

So your the Tabs component should probably look more like this:

const Tabs = ({data, scrollX}) => {
  const [measures, setMeasures] = React.useState([]);
  const containerRef = React.useRef();

  React.useEffect(() => {
    let m = [];
    data.forEach((i) => {
      i.ref.current.measureLayout(
        containerRef.current,
        (x, y, width, height) => {
          m.push({x, y, width, height});
          console.log(x, y, width, height);
        },
      );
      if (m.length === data.length) {
        setMeasures(m);
      }
    });
  }, []);

  return (
    <View style={{position: 'absolute', top: 50, width}}>
      <View
        ref={containerRef}
        style={{justifyContent: 'space-evenly', flex: 1, flexDirection: 'row'}}>
        {data.map((item) => {
          return <Tab key={item.key} item={item} ref={item.ref} />;
        })}
      </View>
      <Indicator />
    </View>
  );
};

Upvotes: 7

Related Questions