Anu
Anu

Reputation: 1742

How to implement a collapsible box in react native?

I am trying to implement a collapsible box in react native.Its working fine for dummy data. But when i tried to list the data response from server i'm getting error.I'm using map method over the response for listing the details.But showing error evaluating this.state.details.map.Also i'm confused to where to place the map method.Below is the code that i've tried.I refer this doc for collapsible box.

Example

class DetailedView extends Component{
    constructor(props){
        super(props);
        this.icons = {
            'up'    : require('../Images/Arrowhead.png'),
            'down'  : require('../Images/Arrowhead-Down.png')
        };

        this.state = {
            title       : props.title,
            expanded    : true,
            animation   : new Animated.Value()
        };
    }
     toggle(){
        let initialValue    = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
            finalValue      = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;

        this.setState({
            expanded : !this.state.expanded
        });

        this.state.animation.setValue(initialValue);
        Animated.spring(
            this.state.animation,
            {
                toValue: finalValue
            }
        ).start();
    }

  _setMaxHeight(event){
        this.setState({
            maxHeight   : event.nativeEvent.layout.height
        });
    }

    _setMinHeight(event){
        this.setState({
            minHeight   : event.nativeEvent.layout.height
        });
    }
    state = {details: []};
    componentWillMount(){
        fetch('https://www.mywebsite.com' + this.props.navigation.state.params.id )
        .then((response) => response.json())
        .then((responseData) =>
          this.setState({
              details:responseData
              })
              );
    }
    render(){
           let icon = this.icons['down'];

        if(this.state.expanded){
            icon = this.icons['up'];
        }


 return this.state.details.map(detail =>
            <Animated.View 
                style={[styles.container,{height: this.state.animation}]}>
                {detail.data.curriculum.map(curr =>
                <View onLayout={this._setMinHeight.bind(this)}>
                <Card>
                <CardSection>
                <View style={styles.thumbnailContainerStyle}>
                <Text style={styles.userStyle}>
                Hii
                </Text>
                </View>
                <TouchableHighlight onPress={this.toggle.bind(this)} 
            underlayColor="#f1f1f1">
                <Image style={styles.buttonImage} source={icon}></Image>
                </TouchableHighlight>
                </CardSection>
                </Card>
                </View>   

                <View style={styles.body} onLayout={this._setMaxHeight.bind(this)}>
                    {this.props.children}   
                    <Card>
                    <CardSection>
                    <Text>{this.props.navigation.state.params.id}</Text>
                    </CardSection>
                    </Card>
                </View>
                )}
            </Animated.View>
        );
  }
}

This is the screenshot for working code with dummy data

Also this is the screenshot of working code with dummy data

Upvotes: 0

Views: 873

Answers (1)

Hisham Mubarak
Hisham Mubarak

Reputation: 1609

1. Solving the Error :

The API call you are making is asynchronous and once the API is called, the code continues to execute before getting the response from the API. The component tries to map through this.state.details before there are any details.

A solution here is that you need to set an ActicityIndicator/Loader initially when component is mounted and once you get the details/response from the API, the state changes and then you can map through this.state.details

Add empty details array to your initial state. state = { details:[] }

Then put your return this.state.details.map(detail.... Inside an if condition like this

if(this.state.details.length > 0) {
<map here>
} else {
return <ActivityLoader />
}

2. Where to place the map methiod

You need to put it inside a function and call that function from within you render method.

showDetailsFunction() {
 return this.state.details.map(detail =>
}

render() {
 return(
   {this.showDetailsFunction()}
 )
}

Upvotes: 1

Related Questions