octopitus / rn-sliding-up-panel

Draggable sliding up panel implemented in React Native https://octopitus.github.io/rn-sliding-up-panel/
MIT License
928 stars 157 forks source link

Multiple Sliders On Same Screen With A Hook Inside Causes Screen Freeze #152

Closed JonnyBoy333 closed 4 years ago

JonnyBoy333 commented 4 years ago

Issue Description

I'm having an issue where I have two sliders on the same screen. I have added the animatedValue per #132 which allows me to have two sliders running simultaneously. The issue occurs when I have a button inside the sliding panel that changes a state using the useState hook. This seems to only occur when the hook is involved, if the button simply alerts something or logs something there is no problem, but if I add a hook in there, there is a problem. After I click a button with a hook and click off the slider instead of animating downward it disappears immediately and everything on screen becomes unresponsive, almost as if the backdrop is still there but invisible.

Steps to Reproduce / Code Snippets / Screenshots

import React, { useState } from 'react';
import { StyleSheet, Text, View, Animated, TouchableOpacity } from 'react-native';
import SlidingUpPanel from 'rn-sliding-up-panel';

const Screen = (props) => {

  const [counter, updateCounter] = useState(0);
  const incrementCounter = () => {
    const newCounter = counter + 1;
    updateCounter(newCounter);
  }
  const animatedValue1 = new Animated.Value(0);
  const animatedValue2 = new Animated.Value(0);

  return (
    <View style={styles.container}>
      <TouchableOpacity onPress={() => this._firstPanel.show()}><Text>Open First Panel</Text></TouchableOpacity>
      <TouchableOpacity onPress={() => this._secondPanel.show()}><Text>Open Second Panel</Text></TouchableOpacity>
      <Text>{counter}</Text>
      <SlidingUpPanel
        height={500}
        draggableRange={{ top: 500, bottom: 0 }}
        ref={(c) => this._firstPanel = c}
        animatedValue={animatedValue1}>
        <View style={styles.slidingPanelContainer}>
          <TouchableOpacity onPress={incrementCounter}><Text>Push Me</Text></TouchableOpacity>
        </View>
      </SlidingUpPanel>
      <SlidingUpPanel
        height={500}
        draggableRange={{ top: 250, bottom: 0 }}
        ref={(c) => this._secondPanel = c}
        animatedValue={animatedValue2}>
        <View style={styles.slidingPanelContainer}>
          <TouchableOpacity onPress={incrementCounter}><Text>Push Me</Text></TouchableOpacity>
        </View>
      </SlidingUpPanel>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center'
  },
  slidingPanelContainer: {
    flex: 1,
    zIndex: 1,
    backgroundColor: '#FFF',
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
    alignItems: 'center',
  }
});

export default Screen;

Sliding Panel Problem

Environment

JonnyBoy333 commented 4 years ago

Just adding a little color to this issue. I also tried converting my component to a class based component with a state and did not experience this issue. So it appears to happen only when I call update state hook functions.

To prove this out I've made a runnable snack with the class and function components you can switch between to see one functioning correctly and the other having problems. https://snack.expo.io/@jonnyboy333/slider-panel-issue

octopitus commented 4 years ago

The problem with the hook version is the animated values are redefined every time state change and the component re-render. It might cause the panel calculates its position wrongly.

Move them into state should resolve the problem.

-  const animatedValue1 = new Animated.Value(0);
-  const animatedValue2 = new Animated.Value(0);
+  const [animatedValue1] = useState(new Animated.Value(0));
+  const [animatedValue2] = useState(new Animated.Value(0));

With the class version, you also moved the animation values outside of render() so it didn't cause the issue.

I would also recommend using useRef to capture component's ref. Example:

const firstPanel = useRef(null);

<Panel ref={firstPanel} />
<TouchableOpacity onPress={() => firstPanel.current.show()} />
JonnyBoy333 commented 4 years ago

Well there you go, still getting used to hooks I guess. That fixes the issue, and thanks for the tip on using the useRef hook.