2

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}
        />
    );
} }
Dave Anderson
  • 438
  • 4
  • 11

3 Answers3

1

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}
          />
      );
  }
}
Rob Walker
  • 877
  • 7
  • 13
0

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> }
   )
}
Sudheesh Singanamalla
  • 2,283
  • 3
  • 19
  • 36
Ishaq Ashraf
  • 159
  • 2
  • 3
  • 10
0

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}
          />
        );
      }
    }