deanmcpherson / react-native-sortable-listview

Drag drop capable wrapper of ListView for React Native
MIT License
917 stars 235 forks source link

undefined is not an object (evaluating 'wrapperLayout.pageY) #111

Closed jpudysz closed 6 years ago

jpudysz commented 6 years ago

Hello, I tried to use latest version from master, but unfortunately it doesn't work. List is rendering correctly, but if I try to drag item I receive an error.

My setup: IPhone 6 (IOS10) or simulator RN 48.3

Code:

<View style={styles.itemInputContainer}>
    <SortableListView
         data={parsedArray}
         order={Object.keys(parsedArray)}
         onRowMoved={console.log}
         renderRow={row => <SortableRow data={row} />}
    />
    <Input
          blurOnSubmit
          style={styles.input}
          onChange={this.handleNewTextInput}
     />
</View>

Sortable row (same code like in examples):

(
    <TouchableHighlight
        underlayColor={'#eee'}
        style={{
            padding: 25,
            backgroundColor: '#F8F8F8',
            borderBottomWidth: 1,
            borderColor: '#eee',
        }}
        {...this.props.sortHandlers}
    >
        <Text>
            {this.props.data.text}
        </Text>
    </TouchableHighlight>
 )

ParsedArray:

const parsedArray = {
    0: {
        text: string
    },
    1: {
        text: string
    },
    ....
}

img_0002 jpg

Issue is related with lines 448-452:

  componentDidMount() {
    InteractionManager.runAfterInteractions(() => {
      setTimeout(this.measureWrapper, 0)
    })
  }

I set debugger inside callback but it was never invoked. Without InteractionManager.runAfterInteractions it's working as expected.

nihgwu commented 6 years ago

Are you using animations (like Animated.loop) in you app? In some cases InteractionManager.runAfterInteractions would never be called if there is a non-stopped animation

jpudysz commented 6 years ago

Yes, that was it! Many thanks. For other users: Check entire navigation stack if any view has not finished animation (in loop). If so, it won't call InteractionManager.runAfterInteractions at any time.

nihgwu commented 6 years ago

Thanks for your feedback, it's good news that you resolved it. I added InteractionManager.runAfterInteractions to make sure the layout measure to be run after the navigation transition

rajeshpanwar007 commented 6 years ago

@jpudysz I am facing same issue and also using animation.

How can I fix it ? Please help

jpudysz commented 6 years ago

@rajeshpanwar007 I was using Animated.loop in the previous screen. What's more screen with SortableListView was stacked above previous screen, because I was using StackNavigator from react-navigation. I resolved it by finding this edge case and disabling animation somehow (I was calling API and I forgot about stopping animation on catch). I did it with something like this (I'm using TS):

class SomeScreen {
  private animation: Animated.CompositeAnimation

  componentDidMount() {
      this.startAnimation()
      this.fetchData()
  }

  componentWillUpdate(nextProps, nextState) {
     // compare states and disable animation
  }

 componentWillUnmount() {
     if (this.animation) {
       this.animation.stop()
     }
 }

  fetchData() {
     this.props.fetchData()
          .then(data => this.setState({
               data,
               shouldDisableAniamtion: true
          }))
         .catch(() => this.setState({
               shouldDisableAniamtion: true
          }))
  }

  startAnimation() {
     this.animation = Animated.loop(/*config*/)

     this.animation.start()
  }
}

Your scenario may vary... just watch your Animations on stacked screens and ensure that you will stop them before user will enter screen with SortableListView.