0

i need to create a variable in a react-native component, which i want to use several times...each instance of this component needs to have a different variable name to reference to.

<View   ref={view => { shapeView = view; }}   
        onLayout={({ nativeEvent }) => { 
            shapeView.measure( (x, y, width, height, pageX, pageY) => {
            console.log('- - - DEBUG: width:' + width + ', pageX:'+ pageX + ', pageY:' + pageY);
            let shapePickerPosition = {w: width, x: pageX, y: pageY};
            setShapeCoords(shapePickerPosition);
            })
        }}>

as i said, i want to use this code inside a component, and this component several times, and if i don't change die variable: "shapeView" i end up with just the coordinates from the last instance of that component..

here's the whole component:


 import React, {useState, useEffect} from 'react';
 import {StyleSheet, View, Text, Modal, TouchableOpacity, Pressable, FlatList} from 'react-native';
 import { useTheme} from '@react-navigation/native';
 
 // ------------------PickerRow-----------------------------------------------------------------------
 function CustomPickerRow(props) {
 
   const { colors } = useTheme(); // works
   const theme = useTheme();
   const [selectedItem, setSelectedItem] = useState('choose');
   const [coordinates, setCoordinates] = useState();
 
   const setItem = (value) => {
       // set parent state
       props.action(value)
   }
 
     return (
       <View
         ref = { view => { shapeView  = view; } }
         onLayout={({ nativeEvent }) => {
             shapeView.measure( (x, y, width, height, pageX, pageY) => {
                 console.log('height:', height);
                 console.log('width:', width);
                 console.log('x:', pageX);
                 console.log('y:', pageY);
                 let coords = {w: width, x: pageX, y: pageY};
                 setCoordinates(coords);
               })
 
         }} 
         style = {{
         flexDirection: 'row', 
         justifyContent: 'space-between', 
         alignItems: 'center', 
         height: 25, 
         paddingLeft: 5, 
         marginBottom: 3, 
         backgroundColor: colors.frameBackground, 
         borderColor: colors.borderColor, 
         borderWidth: 1, 
         borderRadius: 5}}>
           <View style = {styles.icon} >
               <Text style = {styles.text}>{props.icon}</Text>
           </View>
           <View style = {styles.description} >
               <Text style = {{fontSize: 11, fontWeight: 'bold', color: colors.text, textAlign: 'left', marginLeft: 5,}}>{props.title}</Text>
           </View>
           <MyPicker data={props.data} action={setItem} position={coordinates}/>
       </View>
 
       
     );
     }
 
 
   // ------------------MyPicker-----------------------------------------------------------------------
   function MyPicker(props) {
 
     const { colors } = useTheme(); // works
     const theme = useTheme();
     const [isVisible, setIsVisible] = useState(false);
     const [selectedItem, setSelectedItem] = useState(props.data[0].key)
     const [coordinates, setCoordinates] = useState({w: 180, x: 0, y: 0});
 
     useEffect(() => {
         if (props.position) {
             setCoordinates(props.position);
         }
 
     })
   
     const setItem = item => {
       // set parent state
       props.action(item.value);
       setIsVisible(false);
       console.log("chosen value = " + item.key);
       setSelectedItem(item.key);
     }
 
     const showPicker = () => {
         setIsVisible(true);
     }
 
     const renderItem = ({item}) => {
         return <View>
                     <Pressable onPress={() => setItem(item)}>
                         <Text style={{color: colors.text, fontSize: 17, alignSelf: 'center', paddingTop: 3}}>
                             {item.key}
                         </Text>
                     </Pressable>
                 </View>
       }
 
       return (
         <View style={{flex:5, backgroundColor: 'transparent'}}>
             <TouchableOpacity onPress={showPicker}>
                 <Text style={{color: colors.textSubtitleColor, fontSize: 11, alignSelf: 'flex-end', paddingRight: 10}}>
                       {selectedItem}
                 </Text>
             </TouchableOpacity>
 
             <Modal animationType="fade"
                 transparent={true}
                 visible={isVisible}
                 style={styles.testPicker}
                 onRequestClose={() => {
                     console.log('Modal has been closed.');
                 }}
                 >
                     <View style={{  backgroundColor: colors.frameBackground,
                                     borderColor: colors.borderColor, 
                                     borderWidth: 1, 
                                     borderRadius: 5,
                                     position: 'absolute',
                                     width:  180,
                                     height: 200,
                                     left: coordinates.x, //100,
                                     top: coordinates.y //160
                                     }}>
                         <FlatList
                             data={props.data}
                             renderItem={renderItem}
                         />
                     </View>
             </Modal>
         </View>
       );
     }
 
 
     const styles = StyleSheet.create({
 
     testPicker: {
         backgroundColor: 'gray',
         position: 'absolute',
         width: 112,
         height: 200,
         left: 100,
         top: 160
       },
     icon: {
         flex: 1,
         backgroundColor: '#00529F', 
         marginRight: 0, 
         borderRadius: 5
     },
     description: {
         flex: 2,
         height: 17,
         backgroundColor: 'transparent', 
         marginRight: 0, 
         borderRadius: 5
     },
 
   });
 
   export default CustomPickerRow;

and i call that component like this:

<CustomPickerRow id='shapePicker' icon='2' title='Shape:' data={shapeItems} action={setShape} selectedItem={selectedShape} visible={modalVisible} />```
6
  • The only reason you'd need this is if shapeView is outside your component to begin with (which I'll bet it is which is why you are trying to hack it). Post your full component code. Commented Jul 23, 2021 at 19:41
  • na i chose "shapeView" because its just a name for that instance of that component.. my component ist a large one :) i am not sure if posting the whole component would really help, but if necessary of course i will post it Commented Jul 23, 2021 at 19:42
  • 1
    I can almost guarantee you are using global variables instead of instance variables here. See my answer (for functional components) Commented Jul 23, 2021 at 19:44
  • 1
    My point stands, you need to use a ref. You haven't declared shapeView which results in the same behaviour as my SHOULD NOT be like this answer. Once you use a ref, it will work as expected. Commented Jul 23, 2021 at 19:55
  • 1
    shapeView is a global variable - every time a new CustomPickerView gets rendered the single global shapeView variable gets updated. What you need is a shapeView reference per instance of CustomPickerView that is on the screen. The way to do that is in the example I gave below. My initial assumption was right, and the first answer I gave you will solve your problem. Commented Jul 23, 2021 at 23:52

1 Answer 1

1

shapeView should be a ref like this:

const MyComponent = () => {
   const shapeView = useRef();

   return (
     <View
       ref={view => shapeView.current = view}
       /* could also look like this: ref={shapeView} */
       onLayout={({nativeEvent}) => {
          shapeView.current.measure(...);
       }
     />
   )
}

It SHOULD NOT be like this:

let shapeView; // this is outside your component - it shouldn't be here
const MyComponent = () => {

   return (
     <View
       ref={view => shapeView = view}
       onLayout={({nativeEvent}) => {
          shapeView.measure(...);
       }
     />
   )
}

In a class based component it could look like this:


class MyComponent extends React.Component {

    constructor() {
       this.shapeView = React.createRef();
    }

    render() {
       return (
         <View
           ref={this.shapeView}
           onLayout={({nativeEvent}) => {
              this.shapeView.current.measure(...);
           }
         />
       )
    }
}

Sign up to request clarification or add additional context in comments.

4 Comments

no, you're assuming not correctly, i'm sorry as i understood it "shapeView" is a reference to exactly that view..maybe i should really post the whole component ^^
@blendstylez where is shapeView being declared originally?
i'll post the whole component, maybe this will clear things up
you, sir, are an absolute genius! i defined this: const measureView = useRef(); i removed this: ref={view => { measureView = view; }} and inserted this: ref={view => measureView.current = view} onLayout={({ nativeEvent }) => { measureView.current.measure(...) ...that did the job! super precious answer, and i will read the docs to "useRef" (y) BIG THANKS!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.