skip to Main Content

I am trying to change the color of a button pressed from a dynamic json list using useRef. But when I use the useRef it is only aiming at the last value of the list.But if I add array in the list then how would the main component know which element was pressed ?

Need to make the background of the selected item colored and rest white

Parent Element
==> InvoiceTitle is the child component

const Usage = () => {
  const data = UsageDataMock;
  const invoiceTitleRef = useRef("");

  const onChange = () => {
    console.log(
      "Selected Title => ",
      invoiceTitleRef?.current?.getSelectedTitle()
    );
  };

  return (
    <View style={{ flexDirection: "row", marginTop: 50 }}>
      {data.map((item, index) => (
        <InvoiceTitle
          ref={invoiceTitleRef}
          key={item.id}
          title={item.title}
          onChange={onChange}
        />
      ))}
    </View>
  );
};

Child Component

const InvoiceTitle = forwardRef(
  ({ title, onChange, ...props }: InvoiceTitleProps, ref) => {
    const [selectedTitle, setSelectedTitle] = useState("");

    useImperativeHandle(ref, () => ({
      getSelectedTitle: () => selectedTitle,
    }));
    const onClick = (title: string) => {
      onChange(selectedTitle);
    };
    console.log("default title", selectedTitle);

    return (
      <Pressable
        onPress={() => onChange(setSelectedTitle(title))}
        style={({ pressed }) => [
          styles.titleContainer,
          { backgroundColor: "red", opacity: pressed ? 0.5 : 1 },
        ]}
      >
        <AppText>{title}</AppText>
      </Pressable>
    );
  }
);

Updated 28th Nov

Getting selected items but now it is changing color of every button .

const Usage = () => {
  const [data, setData] = useState(UsageDataMock1);
  const invoiceTitleRef = [];

  const onChange = (index, selectedTitle) => {
    // console.log('Selected Title => ', invoiceTitleRef[index].getSelectedTitle())
    const tempSelection = invoiceTitleRef[index].getSelectedTitle();
    const temp = data.map((item, index) => {
      if (item.title == tempSelection) {
        return { ...item, isSelected: "true" };
      } else return { ...item, isSelected: "false" };
    });

    setData(temp);
  };
  console.log("123", data);

  return (
    <AppBackground>
      <View style={{ flexDirection: "row", marginTop: 50 }}>
        {data.map((item, index) => (
          <>
            {console.log("asd", item)}
            <InvoiceTitle
              ref={(element) => (invoiceTitleRef[index] = element)}
              key={item.id}
              title={item.title}
              selected={item?.isSelected}
              onChange={onChange}
              index={index}
            />
          </>
        ))}
      </View>
    </AppBackground>
  );
};

const styles = StyleSheet.create({
  container: {
    flexDirection: "row",
    marginTop: 40,
  },
  titleContainer: {
    marginHorizontal: 10,
    alignSelf: "flex-start",
    paddingHorizontal: 10,
    paddingVertical: 8,
    borderRadius: 12,
  },
});

export default Usage;

Child Component

const InvoiceTitle = forwardRef(
  (
    {
      title,
      onChange,
      index,
      key,
      selected = false,
      ...props
    }: InvoiceTitleProps,
    ref
  ) => {
    useImperativeHandle(ref, () => ({
      getSelectedTitle: () => title,
    }));

    // console.log('ASD title', title, selected)
    // const [color, setColor] = useState('white')

    const [color, setColor] = useState("white");

    const onClick = () => {
      onChange(index, title);
    };

    return (
      <Pressable
        key={key}
        onPress={() => onClick()}
        style={({ pressed }) => [
          styles.titleContainer,
          {
            backgroundColor: selected ? "red" : "blue",
            opacity: pressed ? 0.5 : 1,
          },
        ]}
      >
        <AppText>{title}</AppText>
      </Pressable>
    );
  }
);

const styles = StyleSheet.create({
  container: {
    flexDirection: "row",
    marginTop: 40,
  },
  titleContainer: {
    marginHorizontal: 10,
    alignSelf: "flex-start",
    paddingHorizontal: 10,
    paddingVertical: 8,
    borderRadius: 12,
  },
});

export default InvoiceTitle;

interface InvoiceTitleProps {
  title: string;
  onChange: Function;
  index: number;
  selected: boolean;
  key?: number;
}

2

Answers


  1. Chosen as BEST ANSWER

    Fixed now. the new property that I was adding isSelected : 'true', I kept the true in string but it had to be Boolean that caused this ordeal not to fix.


  2. you are passing a single ref to all so it saves only last value and overwrites all previous ones.
    you have to create as many refs as the length of your items

      import * as React from 'react';
    import { Text, View, StyleSheet,Pressable } from 'react-native';
    import Constants from 'expo-constants';
    
    
    export default function App() {
      const data = [{name:'wqh',id:12},{name:'dj',id:1212}]
      const invoiceTitleRef = [] //Array of refs 
    
      const onChange = (index, selectedTitle) => {
        console.log(index, selectedTitle) //you have your index and selected value of the item
        console.log('Selected Title => ', invoiceTitleRef[index].getSelectedTitle())
      }
    
      return (
        <View style={{ marginTop: 50,flex:1,backgroundColor:'green' }}>
          {data.map((item, index) => (
    
            <InvoiceTitle
              index={index}
              ref={ref => invoiceTitleRef[index]=ref}
              key={item.id} title={item.name}
              onChange={onChange} />
          ))}
        </View>
      )
    }
    
    const InvoiceTitle = React.forwardRef(({
        title,
        onChange,
        index,
        ...props
    }: InvoiceTitleProps, ref) => {
    
        const[selectedTitle, setSelectedTitle] = React.useState('')
    
        React.useImperativeHandle(ref, () => ({
           getSelectedTitle : () => title
        }))
        const onClick = () => {
          setSelectedTitle(title)
          onChange(index,selectedTitle)
        }
      
    
        return (
            <Pressable 
            onPress={onClick} 
                style = {({pressed}) => [styles.titleContainer,
                 {backgroundColor : 'red', opacity : pressed ? 0.5 : 1}]}>
               <Text>{title}</Text>
           </Pressable>
        )
    })
    
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        paddingTop: Constants.statusBarHeight,
        backgroundColor: '#ecf0f1',
        padding: 8,
      },
      paragraph: {
        margin: 24,
        fontSize: 18,
        fontWeight: 'bold',
        textAlign: 'center',
      },
      titleContainer:{
        margin:20,
        padding:20
      }
    });
    

    another way :-
    this way you don’t need to use ref. you have index and value in your onChange method inside parent component and now you know which item is pressed you can save in array or just use it.(don’t need to use any refs )

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search