React-native-ui-kitten: Component inside Tab won't re-render when a prop changes its value.

Created on 8 Jul 2019  ·  9Comments  ·  Source: akveo/react-native-ui-kitten

Issue type

I'm submitting a ...

  • [x] bug report
  • [ ] feature request

Issue description

Current behavior:
When a component prop inside <Tab /> is updated via the redux store, the component doesn't re-render.

Expected behavior:
The component should re-render when the prop changes.

Steps to reproduce:
Add component inside a <Tab />

Related code:

<TabView
    selectedIndex={this.state.selectedIndex}
    onSelect={this.handleTabSelect}
    style={[styles.tabView]}
    tabBarStyle={[styles.tabBarView]}
    indicatorStyle={[styles.tabIndicator]}
>
    <Tab title="Tab 1" titleStyle={[styles.tabTitle, this.state.selectedIndex === 0 && styles.tabTitleSelected]}>
        ...
    </Tab>

    <Tab title="Tab 2" titleStyle={[styles.tabTitle, this.state.selectedIndex === 1 && styles.tabTitleSelected]}>
        ...
    </Tab>

    <Tab title="Tab 3" titleStyle={[styles.tabTitle, this.state.selectedIndex === 2 && styles.tabTitleSelected]}>
        ...
    </Tab>

    <Tab title="Tab 4" titleStyle={[styles.tabTitle, this.state.selectedIndex === 3 && styles.tabTitleSelected]}>
        <MyCustomComponent propFromRedux={propFromRedux} />
    </Tab>
</TabView>

The MyCustomComponent won't re-render when propFromRedux change its value.
To make sure that the problem is not in MyCustomComponent, I've moved it outside the <TabView /> and when the propFromRedux changed, the component re-rendered.

Other information:

OS, device, package version

OS: iOS 12
Device: iPhone Xs
Package version: 4.0.5
Help wanted

Most helpful comment

Thanks! I was just testing this on your playground and it worked.

Keep up the great work!

All 9 comments

Hello @NBoychev ! Thanks for your report.
I've tested the issue and I believe that there is some mistake in your code.
Below you can check a similar working example:

  • GIF:
    Alt Text

  • Code:

  • Container:
import React from 'react';
import {
  NavigationScreenConfig,
  NavigationScreenProps,
} from 'react-navigation';
import { connect } from 'react-redux';
import { TestComp } from './test.component';
import { TopNavigationElement } from '@src/core/navigation/options';
import {
  OrderListHeader,
  OrderListHeaderProps,
} from '@src/components/orders';
import { GlobalState } from '../../store';
import { setOrdersFilterCriteria } from '../../actions';

interface StateProps {
  filterCriteria: string;
  setOrdersFilterCriteria: (criteria: string) => void;
}

type ComponentProps = NavigationScreenProps & StateProps;

const mapStateToProps = (state: GlobalState) => ({
  filterCriteria: state.orders.filterCriteria,
});

const mapDispatchToProps = (dispatch: Function) => ({
  setOrdersFilterCriteria: (criteria: string) => dispatch(setOrdersFilterCriteria(criteria)),
});

@connect(mapStateToProps, mapDispatchToProps)
export class TestContainer extends React.Component<ComponentProps> {

  static navigationOptions: NavigationScreenConfig<any> = ({ navigation, screenProps }) => {
    const ordersHeaderConfig: OrderListHeaderProps = {
      onBack: navigation.getParam('onBack'),
      onMenuItemPress: navigation.getParam('onMenuItemPress'),
      ...navigation,
    };

    const renderHeader = (headerProps: NavigationScreenProps) => {
      return (
        <OrderListHeader
          {...headerProps}
          {...ordersHeaderConfig}
        />
      );
    };

    return {
      ...navigation,
      ...screenProps,
      header: (headerProps: NavigationScreenProps): TopNavigationElement => {
        return renderHeader(headerProps);
      },
    };
  };

  public componentWillMount(): void {
    this.setNavigationParams();
  }

  private setNavigationParams = (): void => {
    this.props.navigation.setParams({
      onBack: this.onBackPress,
      onMenuItemPress: this.onSearchOptionSelect,
    });
  };

  private onBackPress = (): void => {
    this.props.navigation.goBack(null);
  };

  private onSearchOptionSelect = (option: string): void => {
    this.props.setOrdersFilterCriteria(option);
  };

  public render(): React.ReactNode {
    const { filterCriteria } = this.props;

    return (
      <TestComp
        propFromRedux={filterCriteria}
      />
    );
  }
}
  1. Component:
import React from 'react';
import {
  ThemedComponentProps,
  ThemeType,
  withStyles,
} from '@kitten/theme';
import {
  Tab,
  TabView,
  Text,
} from '@kitten/ui';
import { ReduxWaitingTest } from './reduxWaitingTest.component';

interface State {
  selectedIndex: number;
}

interface ComponentProps {
  propFromRedux: string;
}

export type TestComponentProps = ThemedComponentProps & ComponentProps;

class TestComponent extends React.Component<TestComponentProps, State> {

  public state: State = {
    selectedIndex: 0,
  };

  private handleTabSelect = (selectedIndex: number): void => {
    this.setState({ selectedIndex });
  };

  public render(): React.ReactNode {
    const { propFromRedux } = this.props;

    return (
      <TabView
        selectedIndex={this.state.selectedIndex}
        onSelect={this.handleTabSelect}>
        <Tab title='Tab 1'>
          <Text>Tab1</Text>
        </Tab>
        <Tab title='Tab 2'>
          <Text>Tab2</Text>
        </Tab>
        <Tab title='Tab 3'>
          <ReduxWaitingTest propFromRedux={propFromRedux}/>
        </Tab>
      </TabView>
    );
  }
}

export const TestComp = withStyles(TestComponent, (theme: ThemeType) => ({}));
  1. Inner Component:
import React from 'react';
import {
  ThemedComponentProps,
  ThemeType,
  withStyles,
} from '@kitten/theme';
import { Text } from '@kitten/ui';

interface ComponentProps {
  propFromRedux: string;
}

export type ReduxWaitingTestComponentProps = ThemedComponentProps & ComponentProps;

class ReduxWaitingTestComponent extends React.Component<ReduxWaitingTestComponentProps> {

  public render(): React.ReactNode {
    const { propFromRedux } = this.props;
    const value: string = propFromRedux ? propFromRedux : 'Select option';

    return (
      <Text>{value}</Text>
    );
  }
}

export const ReduxWaitingTest = withStyles(ReduxWaitingTestComponent, (theme: ThemeType) => ({}));

@32penkin I've simplified the case and created a snack so you can check the problem:
Screen Recording 2019-07-09 at 03 56 PM

Snack link: https://snack.expo.io/@nboychev/kitten-tab-content-change

@32penkin @artyorsh , let's reopen the issue?

@NBoychev yes, sure. I will deal with this today

We got this fixed in master. ViewPager was refactored to use Animated.View instead of ScrollView. Will be available in the next release. Stay tuned :)

@NBoychev sorry for the misunderstanding. I just used the "master" version of the framework, so everything worked for me :)

Thanks! I was just testing this on your playground and it worked.

Keep up the great work!

@NBoychev You can update to the latest 4.1 version 🎉 Thanks for supporting us!

How about if I use ver 3.1.4?

Was this page helpful?
0 / 5 - 0 ratings