ptomasroos / react-native-scrollable-tab-view

Tabbed navigation that you can swipe between, each tab can have its own ScrollView and maintain its own scroll position between swipes. Pleasantly animated. Customizable tab bar
https://www.npmjs.com/package/react-native-scrollable-tab-view
6.93k stars 2.29k forks source link

tabContainer but no owner was set. #1176

Closed ArunKumarVallal99 closed 1 year ago

ArunKumarVallal99 commented 2 years ago

Error: Element ref was specified as a string (tabContainer) but no owner was set. This could happen for one of the following reasons:

  1. You may be adding a ref to a function component
  2. You may be adding a ref to a component that was not created inside a component's render method
  3. You have multiple copies of React loaded See https://reactjs.org/link/refs-must-have-owner for more information.

This error is located at: in AndroidHorizontalScrollContentView (at ScrollView.js:1107) in AndroidHorizontalScrollView (at ScrollView.js:1238) in ScrollView (at ScrollView.js:1264) in ScrollView (at ScrollableTabBar.js:171) in RCTView (at View.js:34) in View (at ScrollableTabBar.js:167) in ScrollableTabBar (at drList.js:2506) in RCTView (at View.js:34) in View (at react-native-scrollable-tab-view/index.js:396) in ScrollableTabView (at drList.js:2504) in RCTView (at View.js:34) in View (at drList.js:2493) in RCTView (at View.js:34) in View (at SafeAreaView.js:41) in SafeAreaView (at AppView.js:12) in AppView (at drList.js:2492) in DrList (created by Connect(DrList)) in Connect(DrList) (at tab3.js:75) in RCTView (at View.js:34) in View (at tab3.js:49) in RCTView (at View.js:34) in View (at SafeAreaView.js:41) in SafeAreaView (at tab3.js:47) in Tab3 (created by Connect(Tab3)) in Connect(Tab3) (created by SceneView) in SceneView (created by TabNavigationView) in RCTView (at View.js:34) in View (created by ResourceSavingScene) in RCTView (at View.js:34) in View (created by ResourceSavingScene) in ResourceSavingScene (created by TabNavigationView) in RCTView (at View.js:34) in View (at src/index.native.tsx:145) in ScreenContainer (created by TabNavigationView) in RCTView (at View.js:34) in View (created by TabNavigationView) in TabNavigationView (created by NavigationView) in NavigationView (created by Navigator) in Navigator (created by NavigationContainer) in NavigationContainer (at home.js:163) in RCTView (at View.js:34) in View (at home.js:157) in Home (created by Connect(Home)) in Connect(Home) (created by SceneView) in SceneView (created by CardContainer) in RCTView (at View.js:34) in View (created by CardContainer) in RCTView (at View.js:34) in View (created by CardContainer) in RCTView (at View.js:34) in View in CardSheet (created by Card) in RCTView (at View.js:34) in View (at createAnimatedComponent.js:217) in AnimatedComponent (at createAnimatedComponent.js:278) in AnimatedComponentWrapper (created by PanGestureHandler) in PanGestureHandler (created by PanGestureHandler) in PanGestureHandler (created by Card) in RCTView (at View.js:34) in View (at createAnimatedComponent.js:217) in AnimatedComponent (at createAnimatedComponent.js:278) in AnimatedComponentWrapper (created by Card) in RCTView (at View.js:34) in View (created by Card) in Card (created by CardContainer) in CardContainer (created by CardStack) in RCTView (at View.js:34) in View (created by MaybeScreen) in MaybeScreen (created by CardStack) in RCTView (at View.js:34) in View (created by MaybeScreenContainer) in MaybeScreenContainer (created by CardStack) in CardStack in KeyboardManager (created by SafeAreaInsetsContext) in SafeAreaProviderCompat (created by StackView) in RCTView (at View.js:34) in View (at GestureHandlerRootView.android.tsx:21) in GestureHandlerRootView (created by StackView) in StackView (created by StackView) in StackView in Unknown (created by Navigator) in Navigator (created by NavigationContainer) in NavigationContainer (created by SceneView) in SceneView (created by CardContainer) in RCTView (at View.js:34) in View (created by CardContainer) in RCTView (at View.js:34) in View (created by CardContainer) in RCTView (at View.js:34) in View in CardSheet (created by Card) in RCTView (at View.js:34) in View (at createAnimatedComponent.js:217) in AnimatedComponent (at createAnimatedComponent.js:278) in AnimatedComponentWrapper (created by PanGestureHandler) in PanGestureHandler (created by PanGestureHandler) in PanGestureHandler (created by Card) in RCTView (at View.js:34) in View (at createAnimatedComponent.js:217) in AnimatedComponent (at createAnimatedComponent.js:278) in AnimatedComponentWrapper (created by Card) in RCTView (at View.js:34) in View (created by Card) in Card (created by CardContainer) in CardContainer (created by CardStack) in RCTView (at View.js:34) in View (created by MaybeScreen) in MaybeScreen (created by CardStack) in RCTView (at View.js:34) in View (created by MaybeScreenContainer) in MaybeScreenContainer (created by CardStack) in CardStack in KeyboardManager (created by SafeAreaInsetsContext) in RNCSafeAreaProvider (at SafeAreaContext.tsx:76) in SafeAreaProvider (created by SafeAreaInsetsContext) in SafeAreaProviderCompat (created by StackView) in GestureHandlerRootView (at GestureHandlerRootView.android.tsx:26) in GestureHandlerRootView (created by StackView) in StackView (created by StackView) in StackView in Unknown (created by Navigator) in Navigator (created by NavigationContainer) in NavigationContainer (created by SceneView) in SceneView (created by SwitchView) in SwitchView (created by Navigator) in Navigator (created by NavigationContainer) in NavigationContainer (at App.js:21) in Provider (at App.js:20) in App (at CodePush.js:585) in CodePushComponent (at renderApplication.js:47) in RCTView (at View.js:34) in View (at AppContainer.js:107) in RCTView (at View.js:34) in View (at AppContainer.js:134) in AppContainer (at renderApplication.js:40)

I am using the latest react-native-scrollable-tab-view

bitting304 commented 1 year ago

replace the source code by the following node_modules/@react-native-community/viewpager.js `/**

'use strict';

import type { PageScrollEvent, PageScrollStateChangedEvent, PageSelectedEvent, ViewPagerProps, } from './types';

const React = require('react'); const ReactNative = require('react-native');

const {Platform, UIManager} = ReactNative;

const dismissKeyboard = require('react-native/Libraries/Utilities/dismissKeyboard');

import {childrenWithOverriddenStyle} from './utils';

const NativeViewPager = require('./ViewPagerNativeComponent');

const VIEW_PAGER_REF = 'viewPager'; const VIEW_MANAGER_NAME = 'RNCViewPager';

function getViewManagerConfig(viewManagerName) { if (!UIManager.getViewManagerConfig) { // react-native <= 0.57 return UIManager[viewManagerName]; } return UIManager.getViewManagerConfig(viewManagerName); }

/**

class ViewPager extends React.Component { componentDidMount() { // On iOS we do it directly on the native side if (Platform.OS === 'android') { if (this.props.initialPage != null) { this.setPageWithoutAnimation(this.props.initialPage); } } }

/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found

module.exports = ViewPager; scrollabletabBar.js const React = require('react'); const { ViewPropTypes } = ReactNative = require('react-native'); const PropTypes = require('prop-types'); const createReactClass = require('create-react-class'); const { View, Animated, StyleSheet, ScrollView, Text, Platform, Dimensions, } = ReactNative; const Button = require('./Button');

const WINDOW_WIDTH = Dimensions.get('window').width;

const ScrollableTabBar = createReactClass({ propTypes: { goToPage: PropTypes.func, activeTab: PropTypes.number, tabs: PropTypes.array, backgroundColor: PropTypes.string, activeTextColor: PropTypes.string, inactiveTextColor: PropTypes.string, scrollOffset: PropTypes.number, style: ViewPropTypes.style, tabStyle: ViewPropTypes.style, tabsContainerStyle: ViewPropTypes.style, textStyle: Text.propTypes.style, renderTab: PropTypes.func, underlineStyle: ViewPropTypes.style, onScroll: PropTypes.func, },

getDefaultProps() { return { scrollOffset: 52, activeTextColor: 'navy', inactiveTextColor: 'black', backgroundColor: null, style: {}, tabStyle: {}, tabsContainerStyle: {}, underlineStyle: {}, }; },

getInitialState() { this._tabsMeasurements = []; return { _leftTabUnderline: new Animated.Value(0), _widthTabUnderline: new Animated.Value(0), _containerWidth: null, }; },

componentDidMount() { this.props.scrollValue.addListener(this.updateView); },

updateView(offset) { const position = Math.floor(offset.value); const pageOffset = offset.value % 1; const tabCount = this.props.tabs.length; const lastTabPosition = tabCount - 1;

if (tabCount === 0 || offset.value < 0 || offset.value > lastTabPosition) {
  return;
}

if (this.necessarilyMeasurementsCompleted(position, position === lastTabPosition)) {
  this.updateTabPanel(position, pageOffset);
  this.updateTabUnderline(position, pageOffset, tabCount);
}

},

necessarilyMeasurementsCompleted(position, isLastTab) { return this._tabsMeasurements[position] && (isLastTab || this._tabsMeasurements[position + 1]) && this._tabContainerMeasurements && this._containerMeasurements; },

updateTabPanel(position, pageOffset) { const containerWidth = this._containerMeasurements.width; const tabWidth = this._tabsMeasurements[position].width; const nextTabMeasurements = this._tabsMeasurements[position + 1]; const nextTabWidth = nextTabMeasurements && nextTabMeasurements.width || 0; const tabOffset = this._tabsMeasurements[position].left; const absolutePageOffset = pageOffset * tabWidth; let newScrollX = tabOffset + absolutePageOffset;

// center tab and smooth tab change (for when tabWidth changes a lot between two tabs)
newScrollX -= (containerWidth - (1 - pageOffset) * tabWidth - pageOffset * nextTabWidth) / 2;
newScrollX = newScrollX >= 0 ? newScrollX : 0;

if (Platform.OS === 'android') {
  this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false, });
} else {
  const rightBoundScroll = this._tabContainerMeasurements.width - (this._containerMeasurements.width);
  newScrollX = newScrollX > rightBoundScroll ? rightBoundScroll : newScrollX;
  this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false, });
}

},

updateTabUnderline(position, pageOffset, tabCount) { const lineLeft = this._tabsMeasurements[position].left; const lineRight = this._tabsMeasurements[position].right;

if (position < tabCount - 1) {
  const nextTabLeft = this._tabsMeasurements[position + 1].left;
  const nextTabRight = this._tabsMeasurements[position + 1].right;

  const newLineLeft = (pageOffset * nextTabLeft + (1 - pageOffset) * lineLeft);
  const newLineRight = (pageOffset * nextTabRight + (1 - pageOffset) * lineRight);

  this.state._leftTabUnderline.setValue(newLineLeft);
  this.state._widthTabUnderline.setValue(newLineRight - newLineLeft);
} else {
  this.state._leftTabUnderline.setValue(lineLeft);
  this.state._widthTabUnderline.setValue(lineRight - lineLeft);
}

},

renderTab(name, page, isTabActive, onPressHandler, onLayoutHandler) { const { activeTextColor, inactiveTextColor, textStyle, } = this.props; const textColor = isTabActive ? activeTextColor : inactiveTextColor; const fontWeight = isTabActive ? 'bold' : 'normal';

return <Button
  key={`${name}_${page}`}
  accessible={true}
  accessibilityLabel={name}
  accessibilityTraits='button'
  onPress={() => onPressHandler(page)}
  onLayout={onLayoutHandler}
>
  <View style={[styles.tab, this.props.tabStyle, ]}>
    <Text style={[{color: textColor, fontWeight, }, textStyle, ]}>
      {name}
    </Text>
  </View>
</Button>;

},

measureTab(page, event) { const { x, width, height, } = event.nativeEvent.layout; this._tabsMeasurements[page] = {left: x, right: x + width, width, height, }; this.updateView({value: this.props.scrollValue.__getValue(), }); },

render() { const tabUnderlineStyle = { position: 'absolute', height: 4, backgroundColor: 'navy', bottom: 0, };

const dynamicTabUnderline = {
  left: this.state._leftTabUnderline,
  width: this.state._widthTabUnderline,
};

return <View
  style={[styles.container, {backgroundColor: this.props.backgroundColor, }, this.props.style, ]}
  onLayout={this.onContainerLayout}
>
  <ScrollView
    ref={(scrollView) => { this._scrollView = scrollView; }}
    horizontal={true}
    showsHorizontalScrollIndicator={false}
    showsVerticalScrollIndicator={false}
    directionalLockEnabled={true}
    bounces={false}
    scrollsToTop={false}
  >
    <View
      style={[styles.tabs, {width: this.state._containerWidth, }, this.props.tabsContainerStyle, ]}
      // ref={'tabContainer'}
      onLayout={this.onTabContainerLayout}
    >
      {this.props.tabs.map((name, page) => {
        const isTabActive = this.props.activeTab === page;
        const renderTab = this.props.renderTab || this.renderTab;
        return renderTab(name, page, isTabActive, this.props.goToPage, this.measureTab.bind(this, page));
      })}
      <Animated.View style={[tabUnderlineStyle, dynamicTabUnderline, this.props.underlineStyle, ]} />
    </View>
  </ScrollView>
</View>;

},

componentDidUpdate(prevProps) { // If the tabs change, force the width of the tabs container to be recalculated if (JSON.stringify(prevProps.tabs) !== JSON.stringify(this.props.tabs) && this.state._containerWidth) { this.setState({ _containerWidth: null, }); } },

onTabContainerLayout(e) { this._tabContainerMeasurements = e.nativeEvent.layout; let width = this._tabContainerMeasurements.width; if (width < WINDOW_WIDTH) { width = WINDOW_WIDTH; } this.setState({ _containerWidth: width, }); this.updateView({value: this.props.scrollValue.__getValue(), }); },

onContainerLayout(e) { this._containerMeasurements = e.nativeEvent.layout; this.updateView({value: this.props.scrollValue.__getValue(), }); }, });

module.exports = ScrollableTabBar;

const styles = StyleSheet.create({ tab: { height: 49, alignItems: 'center', justifyContent: 'center', paddingLeft: 20, paddingRight: 20, }, container: { height: 50, borderWidth: 1, borderTopWidth: 0, borderLeftWidth: 0, borderRightWidth: 0, borderColor: '#ccc', }, tabs: { flexDirection: 'row', justifyContent: 'space-around', }, }); `

bitting304 commented 1 year ago

1 node_modules/@react-native-community/js/viewpager.js line 96 getInnerViewNode = (): ReactComponent => { return this.pageView.getInnerViewNode(); }; line 152 ref={pageView=>this.pageView=pageView}

2 scrollabletabBar.js line 182 //ref={'tabContainer'}