satya164 / react-native-tab-view

A cross-platform Tab View component for React Native
MIT License
5.13k stars 1.07k forks source link

Clamp `position` Animated Value #560

Closed solomonhawk closed 5 years ago

solomonhawk commented 6 years ago

Current behaviour

I'm seeing a bug in the TabBar behavior. I'm rendering my own custom component here and interpolating the position passed in by react-native-tab-view per suggestion. In a certain edge case I am seeing a position value of -1 which as far as I can tell is invalid. The result is that my tab indicator disappears (it's now transitioned left off-screen) and my tab colors go wacky (since they're not expecting anything outside of the range [0, ... tabs.length - 1].

Reproduction may be a little difficult - I'll see if I can find time to make a snack. Basically to repro I have to swipe to change tabs and then tap on an interactive element within one of my tabs which triggers the issue. I can't quite tell why since the interaction is a tap and not a swipe.

It seems like this is the offending piece: https://github.com/react-native-community/react-native-tab-view/blob/master/src/TabView.js#L85-L87

Expected behaviour

I think that position should respect the following constraint:

0 <= position <= navigationState.routes.length - 1

Is this accurate?

Screenshots (if applicable)

Good:

screen shot 2018-07-13 at 11 57 44 am

Bad:

screen shot 2018-07-13 at 11 57 32 am

What have you tried

I changed the suspect code to the following locally however there's still some odd behavior.

const position = Animated.diffClamp(Animated.multiply(
      Animated.divide(Animated.add(panX, offsetX), layoutXY.x),
      -1
    ), 0, navigationState.routes.length - 1);

Your Environment

software version
ios or android both
react-native 0.55.4
react-native-tab-view ^1.0.2
node 8.11.3
npm or yarn yarn 1.7.0

Below is the source for my TabBar custom component, hope it's helpful:


class Tabs extends React.PureComponent {
  render() {
    let { tabs, position, navigationState, selectTab, selected } = this.props
    let inputRange = navigationState.routes.map((x, i) => i)
    let indicatorWidth = 100 / tabs.length + '%'

    // interpolate tab indicator position
    let indicatorLeftOffset = position.interpolate({
      inputRange,
      outputRange: tabs.map((_, i) => (i * 100) / tabs.length + '%')
    })

    let tabComponents = tabs.map(({ key, label }, index) => {
      // interpolate tab label color between green (active) and navy (inactive)
      let labelColor = position.interpolate({
        inputRange,
        outputRange: inputRange.map(
          i => (i === index ? theme.primaryGreen : theme.primaryNavy)
        )
      })

      return (
        <Tab
          key={key}
          label={label}
          color={labelColor}
          active={key === selected}
          onPress={() => selectTab(key)}
        />
      )
    })

    return (
      <TabsWrapper>
        <TabIndicator
          style={{
            width: indicatorWidth,
            left: indicatorLeftOffset
          }}
        />

        {tabComponents}
      </TabsWrapper>
    )
  }
}```
solomonhawk commented 6 years ago

Update: I'm not convinced this is a bug in the library itself but perhaps some kind of unwanted interaction with some other component of my system. What seemed to trigger it was tapping on a touchable contained within one of the tab views.

I'll try to gather more info.

satya164 commented 6 years ago

Thanks! Curious to see what's causing it.

harunsmrkovic commented 6 years ago

@solomonhawk @satya164 I am experiencing exact same issue... seems like it is being triggered when I change index externally, but I am not sure about it either...

It's a pain as it is not always reproducible :/

Also, besides from sometimes having negative position (which I've solved by adding extrapolate: 'clamp' to the Animated.interpolate, I sometimes get wrong position, such as 2 for a second tab view (where it should be 1).

satya164 commented 5 years ago

Hey, I just released a new alpha 2.0.0-alpha.0 of the library. It's rewritten using react-native-gesture-handler and react-native-reanimated addresses a many platform specific bugs and performance problems. The documentation is updated as well.

Please try the new version and see if it addresses your issue. If not, please open a new issue following the issue template.