1

ScrollView works on iOS Simulator but it doesn't work when it is actually run on Exponent on device. It goes back to the top after touch release. I'm not sure if this is the problem with my code or it is a bug on React Native or Exponent side. Here are the details of my environment:

React Native Version: 0.41

Exponent Version: 14

Link to Exponent application:

https://exp.host/@prez/hairapp

presentation of working ScrollView on iOS Simulator:

https://www.youtube.com/watch?v=3VH-6a-sMok

main.js

import Exponent from 'exponent';
import React from 'react';
import {AppRegistry, Text, Button, View, ScrollView} from 'react-native';
import {StackNavigator} from 'react-navigation';
import Calendar from './modules/calendar';

class CalendarView extends React.Component {
    static navigationOptions = {
        title: 'Calendar'
    }
    render() {
        return (
            <ScrollView>
                <Calendar/>
            </ScrollView>
        )
    }
}

const SimpleApp = StackNavigator({
    Home: {
        screen: CalendarView
    }
});

Exponent.registerRootComponent(SimpleApp);

./modules/calendar/index.js

import React, {Component} from 'react';
import {Text, View, StyleSheet} from 'react-native';
import moment from 'moment';

export default class Calendar extends Component {
    state = {
        reservations: [
            {
                hour: '2017-02-17 00:00',
                duration: 120
            }, {
                hour: '2017-02-17 02:00',
                duration: 60
            }, {
                hour: '2017-02-17 03:00',
                duration: 120
            }, {
                hour: '2017-02-17 05:00',
                duration: 180
            }, {
                hour: '2017-02-17 08:00',
                duration: 120
            }
        ]
    }

    generateIntervalHours() {
        let hours = [];
        // interval has been imported from database configuration, set in minutes
        const interval = 60;
        // current set day, date set only for test purposes
        let it = moment('2017-02-17').hours(0).minutes(0);
        const itEnd = moment(it).hours(23).minutes(59);
        while (it < itEnd) {
            hours.push(moment(it));
            it.add(interval, 'minutes');
        }
        return hours;
    }

    // Function to display hours next to lines/events. Displaying hours and events must be
    // seperated because the line starts in the middle of the hour text and ends in the
    // middle of the next hours text so it would be not possible to display hour and event/line
    // as a single row. Former event block would have to overlay next block to reach middle of the text.
    displayHours = () => (this.generateIntervalHours().map(item => (
        <View key={item.format('HH:mm')} style={styles.hours}>
            <Text style={{
                color: '‎rgb(107, 108, 109)',
                fontSize: 12
            }}>{item.format('HH:mm')}</Text>
        </View>
    )))

    // Function to display lines/events next to hours
    displayTimetable = () => (this.generateIntervalHours().map(hour => {
        const {reservations} = this.state;
        // test current hour if there is reservation
        // that start or lasts during this hour
        const slot = reservations.find(res => {
            const startH = moment(res.hour, 'YYYY-MM-DD HH:mm');
            const endH = moment(startH).add(res.duration, 'minutes');
            return (hour >= startH && hour < endH);
        });
        // no reservation starting with the hour tested
        // no reservation that lasts during the hour tested
        // View with upper border line will be displayed
        // what says that nothing is scheduled for this hour
        if (typeof slot == 'undefined') {
            return (<View key={hour.format('HH:mm')} style={[
                styles.timetable, {
                    height: 60
                }
            ]}/>);
            // reservation found that lasts during the tested hour
            // nothing to display
        } else if (hour.format('YYYY-MM-DD HH:mm') != slot.hour) {
            return null;
            // reservation found with starting hour as tested hour
            // View with height set to duration will be displayed
        } else {
            return <View key={hour.format('HH:mm')} style={[
                styles.timetable, {
                    height: slot.duration,
                    backgroundColor: '‎rgb(32, 127, 227)'
                }
            ]}/>
        };
    }))

    render() {
        return (
            <View style={styles.container}>
                <View>
                    {this.displayHours()}
                </View>
                <View style={styles.timetablePadding}>
                    {this.displayTimetable()}
                </View>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginLeft: 10,
        marginTop: 10,
        marginRight: 10,
        flexDirection: 'row'
    },
    timetablePadding: {
        flex: 1,
        paddingTop: 10
    },
    hours: {
        marginBottom: 40,
        // borderWidth: 1,
        // borderRadius: 2,
        // borderColor: '#ddd',
        marginRight: 10,
        height: 20,
        width: 50,
        justifyContent: 'center'
    },
    timetable: {
        flexDirection: 'row',
        //borderWidth: 1,
        borderRadius: 2,
        //borderColor: '#ddd',
        borderTopColor: '‎rgb(238, 239, 240)',
        borderTopWidth: 1,
        borderBottomWidth: 1,
        borderBottomColor: 'white'
    },
    line: {
        flex: 1,
        height: 1,
        backgroundColor: 'black'
    }
});
Przemek Piechota
  • 1,513
  • 2
  • 12
  • 19
  • Hey Przemek, I think you shouldn't style your calendar container with `flex: 1` since scrollview's only child will be a huge view stretching to all available space. However, if you remove the `flex: 1` in your container, you'll see that it works just fine, since now container knows their children height and therefore scrollview does too. My advice to replace your container view with scrollview, instead of calling your long calendar in a scrollview. It's your calendar needs scroll, isn't it? let me know if that helps, so i can format my answer – eden Feb 19 '17 at 12:59
  • Hi Enie, I removed flex: 1 from container style as you suggested but this still didn't work on a device on Exponent. What I checked later. I have rewritten this case to pure React Native project without Exponent and run it to a real device without Exponent application and... it worked just fine... so it seems it is some Exponent bug probably. I will refactor my code to your suggestions regarding flex and height but it still doesn't answer why it works on RN project and why it doesn't work on Exponent project. – Przemek Piechota Feb 19 '17 at 13:41
  • Yes, I agree, you should better refer this post in react native facebook group, exponent guys may fix this bug. – eden Feb 19 '17 at 13:49

1 Answers1

1

It worked after reinstalling Exponent on a device. Same version before and after reinstallation.

Przemek Piechota
  • 1,513
  • 2
  • 12
  • 19
  • 1
    I found that it's not specifically related to exponent; it can happen with any kind of library. But the fix for me was removing the app completely and then on the next install it works without problem. – Koen. Apr 23 '17 at 14:02