How to change an icon name property with react-native-collapsible onChange()

2k Views Asked by At

I'm using react-native-collapsible to create an accordion. I'm styling the header for each accordion section to look a bit like a list item with some icons, including a chevron. When I tap each section, I'd like to change that section's header chevron from right to down.

I've muddled about with some samples from the "Direct Manipulation" page in the RN docs, and have attempted to employ the use of state variables, but I'm having no luck.

Here's what I've got, and it's telling me onChange() that this.refs['First'] is undefined, though the first chevron's icon ref is "First".

class AccordionView extends React.Component {
constructor(props) {
    super(props);
    //console.log(props);
    this.state = {
        icons: "chevron-right",
    };
}
_renderHeader(section) {
    return (
        <View style={styles.accHeader}>
            <View style={{flex: 1, flexDirection:'row', alignItems: 'center', justifyContent:'flex-start'}}>
                <View style={{flex:.5,flexDirection:'row',alignItems:'center',justifyContent:'flex-start'}}>
                    <Text style={styles.accHeaderText}>{section.title}</Text>
                </View>
                <View style={{flex:.5,flexDirection:'row',alignItems:'center',justifyContent:'flex-end'}}>
                    <FontAwesome name="link" size={24} color="#666" style={{paddingHorizontal:6}} onPress={() => alert('link!')} />
                    <MaterialIcons name="place" size={24} color="#666" style={{paddingHorizontal:6}} />
                    <FontAwesome name="phone" size={24} color="#666" style={{paddingHorizontal:6}} />
                    <FontAwesome name="chevron-right" size={24} color="#999" style={{paddingHorizontal:8}} ref={section.title} />
                </View>
            </View>
        </View>
    )
};
_renderContent(section) {
    return (
        <View style={styles.accContent}>
          <Text>{section.content}</Text>
        </View>
      );
};
_onChange(index) {
    this.refs['First'].setNativeProps({name:"chevron-down"});
};
render() {
    return (
        <Accordion 
            sections={sections} 
            renderHeader={this._renderHeader} 
            renderContent={this._renderContent}
            underlayColor="#0972CE"
            onChange={this._onChange}
        />
    );
} }
3

There are 3 best solutions below

2
On BEST ANSWER

You should store the active index in state, and update the state when a different section becomes active. Then on the icon, check if the index in the state matches the index of the section being rendered, and set the relevant icon.

(I've not been able to test the below code, so I cant guarantee it works, but it should give you the general idea of how it can work.)

class AccordionView extends React.Component {
  constructor(props) {
      super(props);
      //console.log(props);
      this.state = {
        activeIndex: 0,
      };
  }
  _renderHeader(section, index) {
      return (
          <View style={styles.accHeader}>
              <View style={{flex: 1, flexDirection:'row', alignItems: 'center', justifyContent:'flex-start'}}>
                  <View style={{flex:.5,flexDirection:'row',alignItems:'center',justifyContent:'flex-start'}}>
                      <Text style={styles.accHeaderText}>{section.title}</Text>
                  </View>
                  <View style={{flex:.5,flexDirection:'row',alignItems:'center',justifyContent:'flex-end'}}>
                      <FontAwesome name="link" size={24} color="#666" style={{paddingHorizontal:6}} onPress={() => alert('link!')} />
                      <MaterialIcons name="place" size={24} color="#666" style={{paddingHorizontal:6}} />
                      <FontAwesome name="phone" size={24} color="#666" style={{paddingHorizontal:6}} />
                      <FontAwesome name={this.state.activeIndex === index ? "chevron-down" : "chevron-right"} size={24} color="#999" style={{paddingHorizontal:8}} />
                  </View>
              </View>
          </View>
      )
  };
  _renderContent(section) {
      return (
          <View style={styles.accContent}>
            <Text>{section.content}</Text>
          </View>
        );
  };
  _onChange(index) {
    this.setState({
      activeIndex: index,
    })
  };
  render() {
      return (
          <Accordion 
              sections={sections} 
              renderHeader={this._renderHeader} 
              renderContent={this._renderContent}
              underlayColor="#0972CE"
              onChange={this._onChange}
          />
      );
  }
}
1
On

There is a prop which is isActive just pass the prop in header or content component like this below

_renderHeader(section, index, isActive) {
   return(
       {isActive ? <Text>icon 1 </Text> : <Text>icon 2 </Text> }
   )
}
0
On

The 'isActive' prop of React Native collapsible package can be used to achieve this. The implementation is as follows;

 class AccordionView extends React.Component {
      constructor(props) {
        super(props);
        //console.log(props);
        this.state = {
          icons: "chevron-right"
        };
      }
      _renderHeader(section, index, isActive) {
        return (
          <View style={styles.accHeader}>
            <View
              style={{
                flex: 1,
                flexDirection: "row",
                alignItems: "center",
                justifyContent: "flex-start"
              }}
            >
              <View
                style={{
                  flex: 0.5,
                  flexDirection: "row",
                  alignItems: "center",
                  justifyContent: "flex-start"
                }}
              >
                <Text style={styles.accHeaderText}>{section.title}</Text>
              </View>
              <View
                style={{
                  flex: 0.5,
                  flexDirection: "row",
                  alignItems: "center",
                  justifyContent: "flex-end"
                }}
              >
                <FontAwesome
                  name="link"
                  size={24}
                  color="#666"
                  style={{ paddingHorizontal: 6 }}
                  onPress={() => alert("link!")}
                />
                <MaterialIcons
                  name="place"
                  size={24}
                  color="#666"
                  style={{ paddingHorizontal: 6 }}
                />
                <FontAwesome
                  name="phone"
                  size={24}
                  color="#666"
                  style={{ paddingHorizontal: 6 }}
                />
                {isActive ? (
                  <FontAwesome
                    name="chevron-right"
                    size={24}
                    color="#999"
                    style={{ paddingHorizontal: 8 }}
                    ref={section.title}
                  />
                ) : (
                  <FontAwesome
                    name="chevron-down"
                    size={24}
                    color="#999"
                    style={{ paddingHorizontal: 8 }}
                    ref={section.title}
                  />
                )}
              </View>
            </View>
          </View>
        );
      }
      _renderContent(section) {
        return (
          <View style={styles.accContent}>
            <Text>{section.content}</Text>
          </View>
        );
      }
      _onChange(index) {
        this.refs["First"].setNativeProps({ name: "chevron-down" });
      }
      render() {
        return (
          <Accordion
            sections={sections}
            renderHeader={this._renderHeader}
            renderContent={this._renderContent}
            underlayColor="#0972CE"
            onChange={this._onChange}
          />
        );
      }
    }