React-native-collapsible: Invalid redraw after item content updated

Created on 23 Jul 2017  路  9Comments  路  Source: oblador/react-native-collapsible

I have items in accordion, consider it a view with text. When text of an item is updated due to some user action, the item incorrectly redraws - it actually becomes a tiny line.

bug fixed-next-release

Most helpful comment

I have the same problem with Collapsible, but I figured out a workaround (below).

Seems like timing of measurement is not synched with re-renders of the children, when non-collapsed state isn't changing. So the component transitions to a wrong/stale content height (not sure which or why), thus rendering the thin line. If I tell it to remeasure on every componentWillReceiveProps for this use case, the issue goes away:

componentWillReceiveProps(nextProps) {
    if (!nextProps.collapsed && !this.props.collapsed) {
        this.setState({measured: false}, () => this._componentWillReceiveProps(nextProps));
    } else {
        this._componentWillReceiveProps(nextProps);
    }
  }

  _componentWillReceiveProps(nextProps) {
      if (nextProps.collapsed !== this.props.collapsed) {
        this._toggleCollapsed(nextProps.collapsed);
      } else if (
        nextProps.collapsed &&
        nextProps.collapsedHeight !== this.props.collapsedHeight
      ) {
        this.state.height.setValue(nextProps.collapsedHeight);
      }
  }

I'm aware this is a dirty workaround, but it gets the job done for now...

All 9 comments

@reachbeach issued the same problem...

@oblador can you help us with this?

Hey guys, do you have any example code to reproduce this bug?

@oblador shure.

This is a view where I use Accordion. I use react-native-elements for ListItem.

_renderHeader(service) {
    return (
      <View
        style={{ backgroundColor: colors.white }}
      >
        <ListItem
          titleContainerStyle={styles.listItemContainer}
          subtitleContainerStyle={styles.listItemContainer}
          title={service.name}
          subtitle={`0x${service.uuid.substr(4, 4)}`}
          rightIcon={{ name: this.getChevronName(service), type: 'entypo' }}
        />
      </View>
    );
  }

  _renderContent(service) {
    return service.characteristics.map(char => (
      <CharacterItem
        key={char.uuid}
        character={char}
        serviceUUID={service.uuid}
      />
    ));
  }

  render() {
    const { services } = this.props;

    return (
      <View
        style={{ marginTop: 20 }}
      >
        <Accordion
          sections={services}
          underlayColor={'transparent'}
          renderHeader={this._renderHeader}
          renderContent={this._renderContent}
          onChange={(index) => this.setState({ collapsedServiceNumber: index })}
        />
      </View>
    );
  }

CharacterItem:

const CharacterItem = ({ character, getCharacterData, copyCharacterDataToClipboard, shareCharacterData, getCurrCharacterValue, loadStatus }) => {
  const currCharacterValue = getCurrCharacterValue(character.uuid);
  // console.log(currCharacterValue);

  const characterItemHeader = `${getGattCharName(character.uuid)}${currCharacterValue ? `: ${currCharacterValue}` : ''}`;
  // const characterItemHeader = character.uuid;
  const characterItemUUID = `0x${character.uuid.substr(4, 4)}`;

  const CharacterItemComponent = () => (
    <View style={styles.localityItemContainer}>


      <View style={{ flex: 1, flexDirection: 'column' }}>
        <Text style={styles.localityItemHeader}>{characterItemHeader}</Text>
        <Text style={styles.localityItemSecondary}>{characterItemUUID}</Text>
      </View>

      <View style={styles.localityItemView}>
        {getCharacterButton('sync', getCharacterData)}
        {getCharacterButton('copy', copyCharacterDataToClipboard)}
        {getCharacterButton('share', shareCharacterData)}
      </View>

    </View>
  );

  return (
    <ListItem
      component={CharacterItemComponent}
      hideChevron
    />
  );
};

When data, that comes from state gets into CnaracterItem, all data from component dissapear

Before interacting
After touching button in CharacterItem
After reopening Accordion all shown correct

I have looked in code using React Native Debugger, and noticed that all data exists in props. I think this is a problem with rendering native components. Can you help with that?

@oblador can you help with that?

I have the same problem with Collapsible, but I figured out a workaround (below).

Seems like timing of measurement is not synched with re-renders of the children, when non-collapsed state isn't changing. So the component transitions to a wrong/stale content height (not sure which or why), thus rendering the thin line. If I tell it to remeasure on every componentWillReceiveProps for this use case, the issue goes away:

componentWillReceiveProps(nextProps) {
    if (!nextProps.collapsed && !this.props.collapsed) {
        this.setState({measured: false}, () => this._componentWillReceiveProps(nextProps));
    } else {
        this._componentWillReceiveProps(nextProps);
    }
  }

  _componentWillReceiveProps(nextProps) {
      if (nextProps.collapsed !== this.props.collapsed) {
        this._toggleCollapsed(nextProps.collapsed);
      } else if (
        nextProps.collapsed &&
        nextProps.collapsedHeight !== this.props.collapsedHeight
      ) {
        this.state.height.setValue(nextProps.collapsedHeight);
      }
  }

I'm aware this is a dirty workaround, but it gets the job done for now...

@blakew Do a PR ! Works perfectly for my needs !

@blakew saved my day. thank you very much.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

technicallyty picture technicallyty  路  4Comments

jiyarong picture jiyarong  路  8Comments

Korysam15 picture Korysam15  路  4Comments

sohanpatil44 picture sohanpatil44  路  3Comments

pkantsedalov picture pkantsedalov  路  5Comments