1

I've spent hours on this bug and I'm hoping another set of eyes can help me out! Appreciate you're help!

I have a React Native app and inside a Modal, I have a list of options with radio buttons.

What I want: When you click on a TouchableHighlight item inside the model list, I'd like the radio button to move to the item the user presses. In other words, I want to set the radio button as selected if I press on the rowId of the item I clicked.

What is happening: When I close the Modal and reopen, I see that the radio button has moved to the correct option, but the problem is that I need the radio button to move immediately when I click! So, I'm setting the correct state but the component isn't re-rendering until I reopen the Modal.

Here is my custom radio button component:

    export default RadioButton = ({style, selected}) => {
  return (
      <View style={[{
        height: 24,
        width: 24,
        borderRadius: 12,
        borderWidth: 2,
        borderColor: colors.primaryText,
        alignItems: 'center',
        justifyContent: 'center',
      }, style]}>
        {
          selected ?
            <View style={{
              height: 16,
              width: 16,
              borderRadius: 8,
              backgroundColor: colors.primaryText,
            }}/>
            : null
        }
      </View>
  );
}

I have a container component handling the state:

    class ShippingDetails extends Component {
      constructor(props){
        super(props)
        const ds = new ListView.DataSource({rowHasChanged: (r1, r2, ) => r1 !== r2 });
        const deliveryOptions = ['USPS Mail', 'USPS Next Day'] //will be replaces with redux
        this.state = {
          dataSource: ds.cloneWithRows(deliveryOptions),
          shippingMethod: null,
          modalVisible: false,
          selectedOption: 0
        };
      }

      _handleShippingMethodPress = (selected, id) => {
        this.setState({shippingMethod: selected})
        this.setState({selectedOption: id})
      }

      renderShippingMethods = (shippingMethod, sectionID, rowID, highlightRow) => {
        return (
          <SelectShippingMethod 
            onPress={() => this._handleShippingMethodPress(shippingMethod, rowID)}
            shippingMethod={shippingMethod}
            price={'Free Shipping'}
            date={'Expected delivery March 12'}
            selected={this.state.selectedOption == rowID}
          />
        )
      }
  render () {
    return (
      <View style={backgroundApp}>

            <View>
              <SelectShippingMethod 
                shippingMethod={shippingMethod}
                onPress={() => this.setModalVisible(true)}
                price={'Free Shipping'} //replace with redux price
                date={'Expected delivery March 12'}
                singleRow={true}
              />
                <Modal
                  animationType={"slide"} //slide and fade
                  transparent={false}
                  visible={this.state.modalVisible}
                  onRequestClose={()=>{console.warn('callback for onRequestClose Modal in ShippingDetails')}} //
                  >
                  <ShippingMethodOptions 
                    dataSource={this.state.dataSource}
                    renderRow={this.renderShippingMethods}
                    renderSeparator={Separator}
                    closeModal={() => this.setModalVisible(false)} 
                  /> 
                </Modal>
         </View>
      </View>
    )
  }
}

SelectShippingMethod is a component that returns the individual TouchableHighlight Items and is passed selected as a prop from the container component.

return(
        <TouchableHighlight 
            onPress={onPress}>
            <View style={[rowWrapper, {alignItems: 'center', paddingLeft: 5}]}>
                { !singleRow && <RadioButton selected={selected}/>}
                <View style={styles.methods}>
                    <Text style={descriptionColor}>{description}</Text> 
                    <Text style={subHeader}>{shipDate}</Text>
                </View>
                <Text style={styles.price}>{shipPrice}</Text>
                {/*<Icon name="ios-arrow-forward" size={17} color={colors.iconGrey} style={arrows}/>*/}
            </View>
        </TouchableHighlight>
    )

ShippingMethodOptions is a component wraps around the ListView and passes the appropriate props down.

const ShippingMethodOptions = ({dataSource, renderRow, renderSeparator, closeModal}) => {
  return(
        <View style={backgroundApp}>
      <NavHeader closeModal={closeModal}/>
      <Text style={[subHeader, textSection]}></Text>
      <View style={hr}></View>

      <ListView 
        dataSource={dataSource}
        renderRow={renderRow}
        renderSeparator={renderSeparator} 
      />
    </View>
    )
}

I noticed when I upgrade to React 16 with fiber that the radio buttons work as expected, so this makes me wonder a few things.

  1. is fiber just so much faster that I can write buggier code?

  2. is there something different about the life cycle method in the Modal component when using the updated React version?

  3. is there something different about setState that causes more things to re-render with fiber?

UPDATE I've console logged the process and I see that after pressing on an item the renderRow on the ListView is not called again but SelectedShippingMethod is called with an undefined selected. Why does this happen and how can I get this working?

Turnipdabeets
  • 5,815
  • 8
  • 40
  • 63
  • Are you updating the data in the ListView data source? See the second paragraph here: https://facebook.github.io/react-native/docs/listviewdatasource.html – Nathan K May 05 '17 at 23:22
  • @NathanK I'm not updating the data because the data doesn't change, just the radio button changes... maybe I should add the selected option to the data so it changes? – Turnipdabeets May 06 '17 at 01:58

0 Answers0